diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
index 0ad6c829af72..cf7f8932ad61 100644
--- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
+++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest
@@ -1021,6 +1021,10 @@ meth public abstract java.nio.file.Path getImagePath()
CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$AfterRegistrationAccess
outer org.graalvm.nativeimage.hosted.Feature
intf org.graalvm.nativeimage.hosted.Feature$FeatureAccess
+meth public abstract org.graalvm.nativeimage.dynamicaccess.JNIAccess getJNIAccess()
+meth public abstract org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess getReflectiveAccess()
+meth public abstract org.graalvm.nativeimage.dynamicaccess.ResourceAccess getResourceAccess()
+meth public abstract org.graalvm.nativeimage.dynamicaccess.ForeignAccess getForeignAccess()
CLSS public abstract interface static org.graalvm.nativeimage.hosted.Feature$BeforeAnalysisAccess
outer org.graalvm.nativeimage.hosted.Feature
@@ -1161,6 +1165,30 @@ meth public static void addResourceBundle(java.lang.Module,java.lang.String)
meth public static void addResourceBundle(java.lang.Module,java.lang.String,java.util.Locale[])
supr java.lang.Object
+CLSS public abstract interface org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Class>[])
+meth public !varargs abstract void registerForUnsafeAllocation(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Class>[])
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.reflect.Executable[])
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.reflect.Field[])
+meth public !varargs abstract void registerForSerialization(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Class>[])
+meth public !varargs abstract java.lang.Class> registerProxy(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Class>[])
+
+CLSS public abstract interface org.graalvm.nativeimage.dynamicaccess.ResourceAccess
+meth public abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Module,java.lang.String)
+meth public void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.String)
+meth public abstract void registerResourceBundle(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.util.ResourceBundle)
+
+CLSS public abstract interface org.graalvm.nativeimage.dynamicaccess.JNIAccess
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Class>[])
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.reflect.Executable[])
+meth public !varargs abstract void register(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.reflect.Field[])
+
+CLSS public abstract interface org.graalvm.nativeimage.dynamicaccess.ForeignAccess
+meth public !varargs abstract void registerForDirectUpcall(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.invoke.MethodHandle,java.lang.Object,java.lang.Object[])
+meth public !varargs abstract void registerForDowncall(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Object,java.lang.Object[])
+meth public !varargs abstract void registerForUpcall(org.graalvm.nativeimage.dynamicaccess.AccessCondition,java.lang.Object,java.lang.Object[])
+
+
CLSS public final org.graalvm.nativeimage.hosted.RuntimeSerialization
meth public !varargs static void register(java.lang.Class>[])
meth public !varargs static void registerProxyClass(java.lang.Class>[])
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/AccessCondition.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/AccessCondition.java
index 5007d0711547..906325c84eab 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/AccessCondition.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/AccessCondition.java
@@ -44,8 +44,15 @@
/**
* A condition that must be satisfied to register elements for dynamic access (i.e., reflection,
- * serialization, JNI access, resource access, and foreign access at run time). Conditions should be
- * used whenever possible to constrain unnecessary growth of the binary size.
+ * serialization, JNI access, resource access, and foreign access at run time).
+ * {@link AccessCondition} is used for programmatic metadata registration in conjunction with:
+ *
+ * - {@link ReflectiveAccess}
+ * - {@link ResourceAccess}
+ * - {@link JNIAccess}
+ * - {@link ForeignAccess}
+ *
+ * Conditions should be used whenever possible to constrain unnecessary growth of the binary size.
*
* There are currently two types of conditions:
*
@@ -101,7 +108,7 @@ static AccessCondition unconditional() {
* // ConditionalType reached (already initialized) => element access allowed
* }
* }
- *
+ *
* class SuperType {
* static {
* // ConditionalType reached (subtype reached) => element access allowed
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ForeignAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ForeignAccess.java
new file mode 100644
index 000000000000..9d891687e210
--- /dev/null
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ForeignAccess.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.nativeimage.dynamicaccess;
+
+import org.graalvm.nativeimage.hosted.Feature;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+/**
+ * This interface is used to register classes, methods, and fields for foreign access at run time.
+ * An instance of this interface is acquired via
+ * {@link Feature.AfterRegistrationAccess#getForeignAccess()}.
+ *
+ * All methods in {@link ForeignAccess} require a {@link AccessCondition} as their first parameter.
+ * Registration for foreign access will happen only if the specified condition is satisfied.
+ *
+ *
How to use
+ *
+ * {@link ForeignAccess} should only be used during {@link Feature#afterRegistration}. Any attempt
+ * to register metadata in any other phase will result in an error.
+ *
+ * Example:
+ *
+ *
{@code @Override
+ * public void afterRegistration(AfterRegistrationAccess access) {
+ * ForeignAccess foreignAccess = access.getForeignAccess();
+ * AccessCondition condition = AccessCondition.typeReached(ConditionType.class);
+ * foreignAccess.registerForDowncall(condition, java.lang.foreign.ValueLayout.JAVA_INT);
+ * }
+ * }
+ *
+ * @since 25.0.1
+ */
+public interface ForeignAccess {
+
+ /**
+ * Registers the provided function descriptor and options pair at image build time for downcalls
+ * into foreign code, if the {@code condition} is satisfied. Required to get a downcall method
+ * handle using {@link java.lang.foreign.Linker#downcallHandle} for the same descriptor and
+ * options at run time.
+ *
+ * Even though this method is weakly typed for compatibility reasons, run-time checks will be
+ * performed to ensure that the arguments have the expected type. It will be deprecated in favor
+ * of strongly typed variant as soon as possible.
+ *
+ * @param condition represents the condition that needs to be satisfied in order to access
+ * target resources.
+ * @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for downcalls.
+ * @param options An array of {@link java.lang.foreign.Linker.Option} used for the downcalls.
+ *
+ * @since 25.0.1
+ */
+ void registerForDowncall(AccessCondition condition, Object desc, Object... options);
+
+ /**
+ * Registers the provided function descriptor and options pair at image build time for upcalls
+ * from foreign code, if the {@code condition} is satisfied. Required to get an upcall stub
+ * function pointer using {@link java.lang.foreign.Linker#upcallStub} for the same descriptor
+ * and options at run time.
+ *
+ * Even though this method is weakly typed for compatibility reasons, run-time checks will be
+ * performed to ensure that the arguments have the expected type. It will be deprecated in favor
+ * of strongly typed variant as soon as possible.
+ *
+ * @param condition represents the condition that needs to be satisfied in order to access
+ * target resources.
+ * @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for upcalls.
+ * @param options An array of {@link java.lang.foreign.Linker.Option} used for the upcalls.
+ *
+ * @since 25.0.1
+ */
+ void registerForUpcall(AccessCondition condition, Object desc, Object... options);
+
+ /**
+ * Registers a specific static method (denoted by a method handle) as a fast upcall target, if
+ * the {@code condition} is satisfied. This will create a specialized upcall stub that will
+ * invoke only the specified method, which is much faster than using
+ * {@link #registerForUpcall(AccessCondition, Object, Object...)}).
+ *
+ * The provided method handle must be a direct method handle. Those are most commonly created
+ * using {@link java.lang.invoke.MethodHandles.Lookup#findStatic(Class, String, MethodType)}.
+ * However, a strict requirement is that it must be possible to create a non-empty descriptor
+ * for the method handle using {@link MethodHandle#describeConstable()}. The denoted static
+ * method will also be registered for reflective access since run-time code will also create a
+ * method handle to denoted static method.
+ *
+ *
+ * Even though this method is weakly typed for compatibility reasons, run-time checks will be
+ * performed to ensure that the arguments have the expected type. It will be deprecated in favor
+ * of strongly typed variant as soon as possible.
+ *
+ *
+ * @param condition represents the condition that needs to be satisfied in order to access
+ * target resources.
+ * @param target A direct method handle denoting a static method.
+ * @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for upcalls.
+ * @param options An array of {@link java.lang.foreign.Linker.Option} used for the upcalls.
+ *
+ * @since 25.0.1
+ */
+ void registerForDirectUpcall(AccessCondition condition, MethodHandle target, Object desc, Object... options);
+}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/JNIAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/JNIAccess.java
new file mode 100644
index 000000000000..cde9a6f7973b
--- /dev/null
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/JNIAccess.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.nativeimage.dynamicaccess;
+
+import org.graalvm.nativeimage.hosted.Feature;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+
+/**
+ * This interface is used to register classes, methods, and fields for JNI access at runtime. An
+ * instance of this interface is acquired via
+ * {@link Feature.AfterRegistrationAccess#getJNIAccess()}.
+ *
+ * All methods in {@link JNIAccess} require a {@link AccessCondition} as their first parameter. A
+ * class and its members will be registered for JNI access only if the specified condition is
+ * satisfied.
+ *
+ *
How to use
+ *
+ * {@link JNIAccess} should only be used during {@link Feature#afterRegistration}. Any attempt to
+ * register metadata in any other phase will result in an error.
+ *
+ * Example:
+ *
+ *
{@code @Override
+ * public void afterRegistration(AfterRegistrationAccess access) {
+ * JNIAccess jniAccess = access.getJNIAccess();
+ * AccessCondition condition = AccessCondition.typeReached(ConditionType.class);
+ * jniAccess.register(condition, Foo.class);
+ * jniAccess.register(condition, Foo.class.getMethod("method"));
+ * jniAccess.register(condition, Foo.class.getField("field"));
+ * }
+ * }
+ *
+ * @see Java docs -
+ * JNI functions
+ *
+ * @since 25.0.1
+ */
+public interface JNIAccess {
+ /**
+ * Registers the provided classes for JNI access at run time, if the {@code condition} is
+ * satisfied.
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Class>... classes);
+
+ /**
+ * Registers the provided methods for JNI access at run time, if the {@code condition} is
+ * satisfied.
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Executable... methods);
+
+ /**
+ * Registers the provided fields for JNI access at run time, if the {@code condition} is
+ * satisfied.
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Field... fields);
+}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ReflectiveAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ReflectiveAccess.java
new file mode 100644
index 000000000000..abe1d34726d6
--- /dev/null
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ReflectiveAccess.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.nativeimage.dynamicaccess;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+
+import org.graalvm.nativeimage.hosted.Feature;
+
+/**
+ * This interface is used to register classes, methods, and fields for use with the
+ * {@link java.lang.reflect} API at run time, and for serialization at run time. An instance of this
+ * interface is acquired via {@link Feature.AfterRegistrationAccess#getReflectiveAccess()}.
+ *
+ * All methods in {@link ReflectiveAccess} require a {@link AccessCondition} as their first
+ * parameter. A class and its members will be accessible at run-time only if the specified condition
+ * is satisfied.
+ *
+ *
How to use
+ *
+ * {@link ReflectiveAccess} should only be used during {@link Feature#afterRegistration}. Attempts
+ * to register metadata in any other phase of the {@link Feature} API will result in an error.
+ *
+ * Example:
+ *
+ *
{@code @Override
+ * public void afterRegistration(AfterRegistrationAccess access) {
+ * ReflectionAccess reflection = access.getReflectionAccess();
+ *
+ * AccessCondition condition = AccessCondition.typeReached(ConditionType.class);
+ * reflection.register(condition, Foo.class, Bar.class);
+ *
+ * reflection.register(AccessCondition.unconditional(), Foo.class.getMethod("method"));
+ *
+ * reflection.registerForUnsafeAllocation(condition, Foo.class);
+ *
+ * Class> proxyClass = reflection.registerProxy(condition, Interface1.class, Interface2.class);
+ * reflection.registerForSerialization(AccessCondition.unconditional(), proxyClass);
+ * }
+ * }
+ *
+ * @since 25.0.1
+ */
+public interface ReflectiveAccess {
+
+ /**
+ * Registers the provided classes for reflection at run time, if the {@code condition} is
+ * satisfied. This means all reflection methods defined by the {@link java.lang.Class} (except
+ * {@link Class#arrayType()}) are accessible at run time for those classes.
+ *
+ * If a class is not registered for reflection at run time, the following methods will throw
+ * {@link ClassNotFoundException}:
+ *
+ * - {@link Class#forName(String)}
+ * - {@link Class#forName(Module, String)}
+ * - {@link Class#forName(String, boolean, ClassLoader)}
+ * - {@link ClassLoader#loadClass(String)}
+ *
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Class>... classes);
+
+ /**
+ * Registers the provided {@code executables} (methods and constructors) for reflective
+ * invocation at run time, if the {@code condition} is satisfied. This method also registers the
+ * declaring classes of the provided executables for reflection at run time. The executables
+ * will be invocable at run time via
+ * {@link java.lang.reflect.Method#invoke(java.lang.Object, java.lang.Object...)}.
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Executable... executables);
+
+ /**
+ * Registers the provided {@code fields} for reflective access at run time, if the
+ * {@code condition} is satisfied. This method also registers the declaring classes of the
+ * provided fields for reflection at run time.
+ *
+ * The fields will be accessible at run time via following methods:
+ *
+ * - {@link java.lang.reflect.Field#get}
+ *
- {@link java.lang.reflect.Field#set}
+ *
- {@link java.lang.reflect.Field#getByte}
+ * - {@link java.lang.reflect.Field#setByte}
+ * - {@link java.lang.reflect.Field#getChar}
+ * - {@link java.lang.reflect.Field#setChar}
+ * - {@link java.lang.reflect.Field#getShort}
+ * - {@link java.lang.reflect.Field#setShort}
+ * - {@link java.lang.reflect.Field#getInt}
+ * - {@link java.lang.reflect.Field#setInt}
+ * - {@link java.lang.reflect.Field#getLong}
+ * - {@link java.lang.reflect.Field#setLong}
+ * - {@link java.lang.reflect.Field#getFloat}
+ * - {@link java.lang.reflect.Field#setFloat}
+ * - {@link java.lang.reflect.Field#getDouble}
+ * - {@link java.lang.reflect.Field#setDouble}
+ *
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Field... fields);
+
+ /**
+ * Registers the provided classes for serialization at run time, if the {@code condition} is
+ * satisfied. This method also registers the provided classes for reflection at run time.
+ *
+ * The following methods require a type to be registered for serialization:
+ *
+ * - {@link java.io.ObjectOutputStream#writeObject}
+ * - {@link java.io.ObjectOutputStream#writeUnshared}
+ * - {@link java.io.ObjectInputStream#readObject()}
+ * - {@link java.io.ObjectInputStream#readUnshared}
+ *
+ *
+ * @since 25.0.1
+ */
+ void registerForSerialization(AccessCondition condition, Class>... classes);
+
+ /**
+ * Registers a {@link java.lang.reflect.Proxy} class in the system classloader that implements
+ * the specified {@code interfaces}, if the {@code condition} is satisfied. The proxy class is
+ * fully specified by the interfaces it implements, and proxy instances matching that
+ * specification can be created at run time. The returned proxy class can be used in
+ * registration for serialization at run time. NOTE: The order of
+ * the interfaces provided in the {@code interfaces} parameter is significant; different
+ * orderings will produce distinct proxy classes.
+ *
+ * Example:
+ *
+ *
{@code
+ * Class> proxyClass = reflection.registerProxy(AccessCondition.unconditional(), Interface1.class, Interface2.class);
+ * reflection.registerForSerialization(AccessCondition.unconditional(), proxyClass);
+ * }
+ *
+ * @return Proxy class defined by the provided interfaces, or {@code null} if no such proxy
+ * class can be created with the given interfaces
+ *
+ * @since 25.0.1
+ */
+ Class> registerProxy(AccessCondition condition, Class>... interfaces);
+
+ /**
+ * Registers the provided {@code classes} for unsafe allocation at run time, if the
+ * {@code condition} is satisfied. Unsafe allocation can happen via
+ * {@link sun.misc.Unsafe#allocateInstance(Class)} or from native code via
+ * {@code AllocObject(jClass)}.
+ *
+ * @since 25.0.1
+ */
+ void registerForUnsafeAllocation(AccessCondition condition, Class>... classes);
+}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ResourceAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ResourceAccess.java
new file mode 100644
index 000000000000..ee84db128adb
--- /dev/null
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/dynamicaccess/ResourceAccess.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.nativeimage.dynamicaccess;
+
+import java.util.ResourceBundle;
+
+import org.graalvm.nativeimage.hosted.Feature;
+
+/**
+ * This interface is used to register Java resources and {@link java.util.ResourceBundle}s that
+ * should be accessible at run time. An instance of this interface is acquired via
+ * {@link Feature.AfterRegistrationAccess#getResourceAccess()}.
+ *
+ * All methods in {@link ResourceAccess} require a {@link AccessCondition} as their first parameter.
+ * Resources will be registered for run-time access only if the specified condition
is
+ * satisfied.
+ *
+ *
How to use
+ *
+ * {@link ResourceAccess} should only be used during {@link Feature#afterRegistration}. Attempts to
+ * register metadata in any other phase of the {@link Feature} API will result in an error.
+ *
+ * Example:
+ *
+ *
{@code @Override
+ * public void afterRegistration(AfterRegistrationAccess access) {
+ * ResourceAccess resources = access.getResourceAccess();
+ * AccessCondition condition = AccessCondition.typeReached(ConditionType.class);
+ *
+ * resources.register(condition, "example/**");
+ *
+ * resources.register(AccessCondition.unconditional(), "example/directory/concreteResource");
+ *
+ * resources.register(condition, "example/concreteResourceStar\\*");
+ *
+ * try {
+ * ResourceBundle bundle = ResourceBundle.getBundle("bundlePath1");
+ * resources.registerResourceBundle(condition, bundle);
+ * } catch (MissingResourceException e) {
+ * // Log or handle the missing resource
+ * }
+ * }
+ * }
+ *
+ * @since 25.0.1
+ */
+public interface ResourceAccess {
+
+ /**
+ * Registers resources matching the specified {@code glob} in the provided {@code module} for
+ * run-time access, if the {@code condition} is satisfied.
+ *
+ * The {@code glob} uses a subset of
+ * glob-pattern rules for
+ * specifying resources.
+ *
+ * There are several rules to be observed when specifying a resource path:
+ *
+ * - The {@code native-image} tool supports only {@code star (*)} and {@code globstar (**)}
+ * wildcard patterns.
+ *
+ * - By definition, {@code star} can match any number of any characters on one level while
+ * {@code globstar} can match any number of characters at any level.
+ * - If there is a need to treat a star literally (without special meaning), it can be escaped
+ * using {@code \} (for example, {@code \*}).
+ *
+ *
+ * - In the glob, a level represents a part of the pattern separated with
+ * {@code /}.
+ * - When writing glob patterns the following rules must be observed:
+ *
+ * - Glob cannot be empty (for example, {@code ""})
+ * - Glob cannot end with a trailing slash ('/') (for example, {@code "foo/bar/"})
+ * - Glob cannot contain more than two consecutive (non-escaped) {@code *} characters on one
+ * level (for example,
"foo/***/"
)
+ * - Glob cannot contain empty levels (for example, {@code "foo//bar"})
+ * - Glob cannot contain two consecutive globstar wildcards (example,
+ *
"foo/**/**"
)
+ * - Glob cannot have other content on the same level as globstar wildcard (for example,
+ * {@code "foo/**bar/x"})
+ *
+ *
+ *
+ *
+ * Given the following project structure:
+ *
+ *
{@code
+ * app/src/main/resources/{Resource0.txt, Resource1.txt}
+ * }
+ *
+ * You can:
+ *
+ * - Include all resources with glob
**/Resource*.txt
+ * - Include {@code Resource0.txt} with glob
**/Resource0.txt
+ * - Include {@code Resource0.txt} and
Resource1.txt
with globs
+ * **/Resource0.txt
and **/Resource1.txt
+ *
+ *
+ * @param condition the condition that must be satisfied to register the target resource
+ * @param module the Java module instance that contains target resources. If the provided value
+ * is {@code null} or an unnamed module, resources are looked up on the classpath
+ * instead.
+ * @param glob the glob that is matched against all resources
+ *
+ * @since 25.0.1
+ */
+ void register(AccessCondition condition, Module module, String glob);
+
+ /**
+ * Registers resources matching the specified {@code glob} from the classpath for run-time
+ * access, if the {@code condition} is satisfied.
+ *
+ * The {@code glob} uses a subset of
+ * glob-pattern rules for
+ * specifying resources.
+ *
+ * There are several rules to be observed when specifying a resource path:
+ *
+ * - The {@code native-image} tool supports only {@code star (*)} and {@code globstar (**)}
+ * wildcard patterns.
+ *
+ * - By definition, {@code star} can match any number of any characters on one level while
+ * {@code globstar} can match any number of characters at any level.
+ * - If there is a need to treat a star literally (without special meaning), it can be escaped
+ * using {@code \} (for example, {@code \*}).
+ *
+ *
+ * - In the glob, a level represents a part of the pattern separated with
+ * {@code /}.
+ * - When writing glob patterns the following rules must be observed:
+ *
+ * - Glob cannot be empty (for example, {@code ""})
+ * - Glob cannot end with a trailing slash ('/') (for example, {@code "foo/bar/"})
+ * - Glob cannot contain more than two consecutive (non-escaped) {@code *} characters on one
+ * level (for example,
"foo/***/"
)
+ * - Glob cannot contain empty levels (for example, {@code "foo//bar"})
+ * - Glob cannot contain two consecutive globstar wildcards (example,
+ *
"foo/**/**"
)
+ * - Glob cannot have other content on the same level as globstar wildcard (for example,
+ * {@code "foo/**bar/x"})
+ *
+ *
+ *
+ *
+ * Given the following project structure:
+ *
+ * {@code
+ * app/src/main/resources/{Resource0.txt, Resource1.txt}
+ * }
+ *
+ * You can:
+ *
+ * - Include all resources with glob
**/Resource*.txt
+ * - Include {@code Resource0.txt} with glob
**/Resource0.txt
+ * - Include {@code Resource0.txt} and
Resource1.txt}
with globs
+ * **/Resource0.txt
and **/Resource1.txt
+ *
+ *
+ * @param condition the condition that must be satisfied to register the target resource
+ *
+ * @param glob the glob that is matched against all resources
+ *
+ * @since 25.0.1
+ */
+ default void register(AccessCondition condition, String glob) {
+ register(condition, null, glob);
+ }
+
+ /**
+ * Registers the provided {@link java.util.ResourceBundle}s for run-time access, if the
+ * {@code condition} is satisfied. Each {@link java.util.ResourceBundle} must be obtained using
+ * any version of the {@link java.util.ResourceBundle#getBundle} method. A bundle created in any
+ * other way will result in an error.
+ *
+ * Recommended use to avoid crashes during image building:
+ *
+ *
{@code @Override
+ * public void afterRegistration(AfterRegistrationAccess access) {
+ * ResourceAccess resources = access.getResourceAccess();
+ * AccessCondition condition = AccessCondition.typeReached(ConditionType.class);
+ * try {
+ * ResourceBundle bundle1 = ResourceBundle.getBundle("bundlePath1");
+ * ResourceBundle bundle2 = ResourceBundle.getBundle("bundlePath2");
+ * resources.registerResourceBundle(condition, bundle1, bundle2);
+ * } catch (MissingResourceException e) {
+ * // Log or handle the missing resource
+ * }
+ * }
+ * }
+ *
+ * @since 25.0.1
+ */
+ void registerResourceBundle(AccessCondition condition, ResourceBundle... bundles);
+}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java
index 67430aefd108..aee207e8c5e7 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java
@@ -54,6 +54,10 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ResourceAccess;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
+import org.graalvm.nativeimage.dynamicaccess.JNIAccess;
+import org.graalvm.nativeimage.dynamicaccess.ForeignAccess;
/**
* Features allow clients to intercept the native image generation and run custom initialization
@@ -171,6 +175,32 @@ interface IsInConfigurationAccess extends FeatureAccess {
*/
@Platforms(Platform.HOSTED_ONLY.class)
interface AfterRegistrationAccess extends FeatureAccess {
+
+ /**
+ * Returns the instance of {@link RuntimeReflection} used to register elements for
+ * reflection at run time. All registrations should happen in
+ * {@link Feature#afterRegistration}.
+ */
+ ReflectiveAccess getReflectiveAccess();
+
+ /**
+ * Returns the instance of {@link RuntimeResourceAccess} used to register resources for run
+ * time access. All registrations should happen in {@link Feature#afterRegistration}.
+ */
+ ResourceAccess getResourceAccess();
+
+ /**
+ * Returns the instance of {@link RuntimeJNIAccess} used to register elements for JNI access
+ * at run time. All registrations should happen in {@link Feature#afterRegistration}.
+ */
+ JNIAccess getJNIAccess();
+
+ /**
+ * Returns the instance of {@link RuntimeForeignAccess} used to register elements for
+ * foreign access at run time. All registrations should happen in
+ * {@link Feature#afterRegistration}.
+ */
+ ForeignAccess getForeignAccess();
}
/**
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
index 1fcd5738fddf..e42a60fdda68 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeForeignAccess.java
@@ -46,13 +46,16 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ForeignAccess;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.impl.RuntimeForeignAccessSupport;
@Platforms(Platform.HOSTED_ONLY.class)
-@SuppressWarnings("all")
public final class RuntimeForeignAccess {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Registers the provided function descriptor and options pair at image build time for downcalls
* into foreign code. Required to get a downcall method handle using
@@ -62,6 +65,8 @@ public final class RuntimeForeignAccess {
* Even though this method is weakly typed for compatibility reasons, runtime checks will be
* performed to ensure that the arguments have the expected type. It will be deprecated in favor
* of strongly typed variant as soon as possible.
+ *
+ * This API is deprecated; use the {@link ForeignAccess} instead.
*
* @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for downcalls.
* @param options An array of {@link java.lang.foreign.Linker.Option} used for the downcalls.
@@ -69,6 +74,7 @@ public final class RuntimeForeignAccess {
* @since 23.1
*/
public static void registerForDowncall(Object desc, Object... options) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeForeignAccessSupport.class).registerForDowncall(AccessCondition.unconditional(), desc, options);
}
@@ -80,6 +86,8 @@ public static void registerForDowncall(Object desc, Object... options) {
* Even though this method is weakly typed for compatibility reasons, runtime checks will be
* performed to ensure that the arguments have the expected type. It will be deprecated in favor
* of strongly typed variant as soon as possible.
+ *
+ * This API is deprecated; use the {@link ForeignAccess} instead.
*
* @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for upcalls.
* @param options An array of {@link java.lang.foreign.Linker.Option} used for the upcalls.
@@ -87,6 +95,7 @@ public static void registerForDowncall(Object desc, Object... options) {
* @since 24.1
*/
public static void registerForUpcall(Object desc, Object... options) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeForeignAccessSupport.class).registerForUpcall(AccessCondition.unconditional(), desc, options);
}
@@ -107,6 +116,8 @@ public static void registerForUpcall(Object desc, Object... options) {
* performed to ensure that the arguments have the expected type. It will be deprecated in favor
* of strongly typed variant as soon as possible.
*
+ *
+ * This API is deprecated; use the {@link ForeignAccess} instead.
*
* @param target A direct method handle denoting a static method.
* @param desc A {@link java.lang.foreign.FunctionDescriptor} to register for upcalls.
@@ -115,6 +126,7 @@ public static void registerForUpcall(Object desc, Object... options) {
* @since 24.2
*/
public static void registerForDirectUpcall(MethodHandle target, Object desc, Object... options) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeForeignAccessSupport.class).registerForDirectUpcall(AccessCondition.unconditional(), target, desc, options);
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeJNIAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeJNIAccess.java
index 5b01d73e9db9..dd4a5e6276b9 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeJNIAccess.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeJNIAccess.java
@@ -46,6 +46,8 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.JNIAccess;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
@@ -58,14 +60,19 @@
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeJNIAccess {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Makes the provided classes available for JNI access at run time. Needed when native code
* looks up Java classes via FindClass.
+ *
+ * This API is deprecated; use the {@link JNIAccess} instead.
*
* @since 22.3
*/
public static void register(Class>... classes) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(AccessCondition.unconditional(), classes);
}
@@ -75,10 +82,13 @@ public static void register(Class>... classes) {
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid">GetMethodID
* or GetStaticMethodID.
+ *
+ * This API is deprecated; use the {@link JNIAccess} instead.
*
* @since 22.3
*/
public static void register(Executable... methods) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(AccessCondition.unconditional(), false, methods);
}
@@ -88,10 +98,13 @@ public static void register(Executable... methods) {
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getfieldid">GetFieldID
* or GetStaticFieldID.
+ *
+ * This API is deprecated; use the {@link JNIAccess} instead.
*
* @since 22.3
*/
public static void register(Field... fields) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(AccessCondition.unconditional(), false, fields);
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeProxyCreation.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeProxyCreation.java
index def50883e26b..d671828d89f0 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeProxyCreation.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeProxyCreation.java
@@ -43,7 +43,9 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
/**
@@ -54,14 +56,19 @@
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeProxyCreation {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Enables registering specifications of {@link java.lang.reflect.Proxy} classes during the
* image build so that matching proxy objects can be created at runtime. The proxy class is
* fully specified by the interfaces it implements.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 22.3
*/
public static void register(Class>... interfaces) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeProxyRegistrySupport.class).registerProxy(AccessCondition.unconditional(), interfaces);
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java
index 52b3506745fd..6d16bb7acb32 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeReflection.java
@@ -48,7 +48,9 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
//Checkstyle: allow reflection
@@ -62,13 +64,18 @@
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeReflection {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Makes the provided classes available for reflection at run time. A call to
* {@link Class#forName} for the names of the classes will return the classes at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 19.0
*/
public static void register(Class>... classes) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), classes);
}
@@ -76,10 +83,13 @@ public static void register(Class>... classes) {
* Makes the provided class available for reflection at run time. A call to
* {@link Class#forName} for the name of the class will return the class (if it exists) or a
* {@link ClassNotFoundException} at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerClassLookup(String className) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerClassLookup(AccessCondition.unconditional(), className);
}
@@ -87,10 +97,13 @@ public static void registerClassLookup(String className) {
* Makes the provided methods available for reflection at run time. The methods will be returned
* by {@link Class#getMethod}, {@link Class#getDeclaredMethod(String, Class[])}, and all the
* other methods on {@link Class} that return a single method.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 19.0
*/
public static void register(Executable... methods) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, methods);
}
@@ -99,10 +112,13 @@ public static void register(Executable... methods) {
* returned by {@link Class#getMethod}, {@link Class#getDeclaredMethod(String, Class[])}, and
* all the other methods on {@link Class} that return a single method, but will not be invocable
* and will not be considered reachable.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 21.3
*/
public static void registerAsQueried(Executable... methods) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), true, methods);
}
@@ -112,10 +128,13 @@ public static void registerAsQueried(Executable... methods) {
* all the other methods on {@link Class} that return a single method, but will not be invocable
* and will not be considered reachable. If the method doesn't exist a
* {@link NoSuchMethodException} will be thrown when calling these methods at run-time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerMethodLookup(Class> declaringClass, String methodName, Class>... parameterTypes) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), declaringClass, methodName, parameterTypes);
}
@@ -126,10 +145,13 @@ public static void registerMethodLookup(Class> declaringClass, String methodNa
* that return a single constructor, but will not be invocable and will not be considered
* reachable. If the constructor doesn't exist a {@link NoSuchMethodException} will be thrown
* when calling these methods at run-time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerConstructorLookup(Class> declaringClass, Class>... parameterTypes) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerConstructorLookup(AccessCondition.unconditional(), declaringClass, parameterTypes);
}
@@ -137,10 +159,13 @@ public static void registerConstructorLookup(Class> declaringClass, Class>..
* Makes the provided fields available for reflection at run time. The fields will be returned
* by {@link Class#getField}, {@link Class#getDeclaredField(String)},and all the other methods
* on {@link Class} that return a single field.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 19.0
*/
public static void register(Field... fields) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, fields);
}
@@ -149,124 +174,163 @@ public static void register(Field... fields) {
* {@link Class#getField}, {@link Class#getDeclaredField(String)}, and all the other methods on
* {@link Class} that return a single field. If the field doesn't exist a
* {@link NoSuchFieldException} will be thrown when calling these methods at run-time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 19.0
*/
public static void registerFieldLookup(Class> declaringClass, String fieldName) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerFieldLookup(AccessCondition.unconditional(), declaringClass, fieldName);
}
/**
* Allows calling {@link Class#getClasses()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllClasses(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllClassesQuery(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getDeclaredClasses()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllDeclaredClasses(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredClassesQuery(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getMethods()} on the provided class at run time. The methods will
* also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllMethods(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllMethodsQuery(AccessCondition.unconditional(), true, declaringClass);
}
/**
* Allows calling {@link Class#getDeclaredMethods()} on the provided class at run time. The
* methods will also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllDeclaredMethods(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredMethodsQuery(AccessCondition.unconditional(), true, declaringClass);
}
/**
* Allows calling {@link Class#getConstructors()} on the provided class at run time. The
* constructors will also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllConstructors(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllConstructorsQuery(AccessCondition.unconditional(), true, declaringClass);
}
/**
* Allows calling {@link Class#getDeclaredConstructors()} on the provided class at run time. The
* constructors will also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllDeclaredConstructors(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(AccessCondition.unconditional(), true, declaringClass);
}
/**
* Allows calling {@link Class#getFields()} on the provided class at run time. The fields will
* also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllFields(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllFields(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getDeclaredFields()} on the provided class at run time. The
* fields will also be registered for individual queries.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllDeclaredFields(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getNestMembers()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllNestMembers(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllNestMembersQuery(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getPermittedSubclasses()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllPermittedSubclasses(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllPermittedSubclassesQuery(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getRecordComponents()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllRecordComponents(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllRecordComponentsQuery(AccessCondition.unconditional(), declaringClass);
}
/**
* Allows calling {@link Class#getSigners()} on the provided class at run time.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 23.0
*/
public static void registerAllSigners(Class> declaringClass) {
+ deprecationFlag.printDeprecationWarning();
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllSignersQuery(AccessCondition.unconditional(), declaringClass);
}
@@ -296,10 +360,13 @@ public static void register(boolean finalIsWritable, boolean allowUnsafeAccess,
* Makes the provided classes available for reflective instantiation by
* {@link Class#newInstance}. This is equivalent to registering the nullary constructors of the
* classes.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 19.0
*/
public static void registerForReflectiveInstantiation(Class>... classes) {
+ deprecationFlag.printDeprecationWarning();
for (Class> clazz : classes) {
if (clazz.isArray() || clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
throw new IllegalArgumentException("Class " + clazz.getTypeName() + " cannot be instantiated reflectively. It must be a non-abstract instance type.");
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java
index 3ae35601d993..fef6353bcd03 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java
@@ -47,25 +47,30 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ResourceAccess;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
/**
* This class can be used to register Java resources and ResourceBundles that should be accessible
* at run time.
- *
- * @since 22.3
*/
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeResourceAccess {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Make Java resource {@code resourcePath} from {@code module} available at run time. If the
* given {@code module} is unnamed, the resource is looked up on the classpath instead.
+ *
+ * This API is deprecated; use the {@link ResourceAccess} instead.
*
* @since 22.3
*/
public static void addResource(Module module, String resourcePath) {
+ deprecationFlag.printDeprecationWarning();
Objects.requireNonNull(module);
Objects.requireNonNull(resourcePath);
ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourcePath, "Manually added via RuntimeResourceAccess");
@@ -76,10 +81,13 @@ public static void addResource(Module module, String resourcePath) {
* {@code resourceContent}. At runtime the resource can be accessed as if it was part of the
* original application. If the given {@code module} is unnamed, the resource is placed on the
* classpath instead.
+ *
+ * This API is deprecated; use the {@link ResourceAccess} instead.
*
* @since 22.3
*/
public static void addResource(Module module, String resourcePath, byte[] resourceContent) {
+ deprecationFlag.printDeprecationWarning();
Objects.requireNonNull(module);
Objects.requireNonNull(resourcePath);
Objects.requireNonNull(resourceContent);
@@ -91,10 +99,13 @@ public static void addResource(Module module, String resourcePath, byte[] resour
* Make Java ResourceBundle that is specified by a {@code baseBundleName} and {@code locales}
* from module {@code module} available at run time. If the given {@code module} is unnamed, the
* ResourceBundle is looked up on the classpath instead.
+ *
+ * This API is deprecated; use the {@link ResourceAccess} instead.
*
* @since 22.3
*/
public static void addResourceBundle(Module module, String baseBundleName, Locale[] locales) {
+ deprecationFlag.printDeprecationWarning();
Objects.requireNonNull(locales);
RuntimeResourceSupport.singleton().addResourceBundles(AccessCondition.unconditional(),
withModuleName(module, baseBundleName), Arrays.asList(locales));
@@ -104,10 +115,13 @@ public static void addResourceBundle(Module module, String baseBundleName, Local
* Make Java ResourceBundle that is specified by a {@code bundleName} from module {@code module}
* available at run time. If the given {@code module} is unnamed, the ResourceBundle is looked
* up on the classpath instead.
+ *
+ * This API is deprecated; use the {@link ResourceAccess} instead.
*
* @since 22.3
*/
public static void addResourceBundle(Module module, String bundleName) {
+ deprecationFlag.printDeprecationWarning();
RuntimeResourceSupport.singleton().addResourceBundles(AccessCondition.unconditional(),
withModuleName(module, bundleName));
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java
index efe096fd128c..71cad1c626bf 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeSerialization.java
@@ -40,8 +40,11 @@
*/
package org.graalvm.nativeimage.hosted;
+import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
@@ -54,6 +57,8 @@
@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeSerialization {
+ private static final APIDeprecationSupport deprecationFlag = ImageSingletons.lookup(APIDeprecationSupport.class);
+
/**
* Register the specified serialization target class itself and all associated classes.
*
@@ -67,20 +72,26 @@ public final class RuntimeSerialization {
* Another limitation is the specified {@code clazz} must have no subclasses (effectively
* final). Otherwise, the actual serialization target class could be any subclass of the
* specified class at runtime.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @param clazz the serialization target class
* @since 21.3
*/
public static void registerIncludingAssociatedClasses(Class> clazz) {
+ deprecationFlag.printDeprecationWarning();
RuntimeSerializationSupport.singleton().registerIncludingAssociatedClasses(AccessCondition.unconditional(), clazz);
}
/**
* Makes the provided classes available for serialization at runtime.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 21.3
*/
public static void register(Class>... classes) {
+ deprecationFlag.printDeprecationWarning();
for (Class> clazz : classes) {
RuntimeSerializationSupport.singleton().register(AccessCondition.unconditional(), clazz);
}
@@ -110,10 +121,13 @@ public static void registerWithTargetConstructorClass(Class> clazz, Class> c
* Makes a class available for serialization at runtime that is created for the lambda
* expressions (a class that has a $deserializeLambda$ method) specified by the
* lambdaCapturingClass.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 22.3
*/
public static void registerLambdaCapturingClass(Class> lambdaCapturingClass) {
+ deprecationFlag.printDeprecationWarning();
RuntimeSerializationSupport.singleton().registerLambdaCapturingClass(AccessCondition.unconditional(), lambdaCapturingClass);
}
@@ -121,10 +135,13 @@ public static void registerLambdaCapturingClass(Class> lambdaCapturingClass) {
* Makes a dynamic proxy class (class that extends {@link java.lang.reflect.Proxy}) available
* for serialization at runtime that is specified by the given interfaces the proxy class
* implements.
+ *
+ * This API is deprecated; use the {@link ReflectiveAccess} instead.
*
* @since 22.3
*/
public static void registerProxyClass(Class>... implementedInterfaces) {
+ deprecationFlag.printDeprecationWarning();
RuntimeSerializationSupport.singleton().registerProxyClass(AccessCondition.unconditional(), implementedInterfaces);
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/APIDeprecationSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/APIDeprecationSupport.java
new file mode 100644
index 000000000000..0082da5c66c9
--- /dev/null
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/APIDeprecationSupport.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * The Universal Permissive License (UPL), Version 1.0
+ *
+ * Subject to the condition set forth below, permission is hereby granted to any
+ * person obtaining a copy of this software, associated documentation and/or
+ * data (collectively the "Software"), free of charge and under any and all
+ * copyright rights in the Software, and any and all patent rights owned or
+ * freely licensable by each licensor hereunder covering either (i) the
+ * unmodified Software as contributed to or provided by such licensor, or (ii)
+ * the Larger Works (as defined below), to deal in both
+ *
+ * (a) the Software, and
+ *
+ * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
+ * one is included with the Software each a "Larger Work" to which the Software
+ * is contributed by such licensors),
+ *
+ * without restriction, including without limitation the rights to copy, create
+ * derivative works of, display, perform, and distribute the Software and make,
+ * use, sell, offer for sale, import, export, have made, and have sold the
+ * Software and the Larger Work(s), and to sublicense the foregoing rights on
+ * either these or other terms.
+ *
+ * This license is subject to the following condition:
+ *
+ * The above copyright notice and either this complete permission notice or at a
+ * minimum a reference to the UPL must be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.graalvm.nativeimage.impl;
+
+/**
+ * A class that is used to emit warnings when calling the APIs that are currently in the migration
+ * process, if an appropriate flag is enabled. It is used in conjunction with
+ * {@link org.graalvm.nativeimage.ImageSingletons}.
+ */
+public class APIDeprecationSupport {
+ private final boolean flagValue;
+ private boolean userEnabledFeaturesStarted = false;
+
+ public APIDeprecationSupport(boolean flagValue) {
+ this.flagValue = flagValue;
+ }
+
+ public boolean isUserEnabledFeaturesStarted() {
+ return userEnabledFeaturesStarted;
+ }
+
+ public void setUserEnabledFeaturesStarted(boolean started) {
+ userEnabledFeaturesStarted = started;
+ }
+
+ public void printDeprecationWarning() {
+ if (flagValue && userEnabledFeaturesStarted) {
+ StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
+ String callersClass = stackTrace[2].getClassName();
+ String migrationClass;
+ switch (callersClass) {
+ case "org.graalvm.nativeimage.hosted.RuntimeResourceAccess":
+ migrationClass = "ResourceAccess";
+ break;
+ case "org.graalvm.nativeimage.hosted.RuntimeJNIAccess":
+ migrationClass = "JNIAccess";
+ break;
+ case "org.graalvm.nativeimage.hosted.RuntimeForeignAccess":
+ migrationClass = "ForeignAccess";
+ break;
+ default:
+ migrationClass = "ReflectiveAccess";
+ break;
+ }
+
+ System.err.println(
+ String.format("Warning: You are using an outdated metadata registration API. Please migrate to the new API: %s located in the 'dynamicaccess' package.", migrationClass));
+ for (int i = 2; i < stackTrace.length; i++) {
+ System.err.println("\tat " + stackTrace[i]);
+ }
+ }
+ }
+}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java
index 801db9f44d60..a491f4a7b5a5 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/ReflectionRegistry.java
@@ -48,10 +48,10 @@
public interface ReflectionRegistry {
default void register(AccessCondition condition, Class>... classes) {
- Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz));
+ Arrays.stream(classes).forEach(clazz -> register(condition, clazz));
}
- void register(AccessCondition condition, boolean unsafeAllocated, Class> clazz);
+ void register(AccessCondition condition, Class> clazz);
void register(AccessCondition condition, boolean queriedOnly, Executable... methods);
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java
index 510d69d4e452..3abfbe55c766 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java
@@ -52,6 +52,10 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
void registerAllDeclaredFields(AccessCondition condition, Class> clazz);
+ void registerAllFieldsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz);
+
+ void registerAllDeclaredFieldsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz);
+
void registerAllConstructorsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz);
void registerAllDeclaredConstructorsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz);
@@ -70,4 +74,5 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
void registerClassLookupException(AccessCondition condition, String typeName, Throwable t);
+ void registerUnsafeAllocation(AccessCondition condition, Class>... classes);
}
diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeSerializationSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeSerializationSupport.java
index 9515617dc4d5..263402fc74b1 100644
--- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeSerializationSupport.java
+++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeSerializationSupport.java
@@ -56,6 +56,10 @@ static RuntimeSerializationSupport singleton() {
void registerIncludingAssociatedClasses(C condition, Class> clazz);
+ default void register(C condition, Class>... classes) {
+ Arrays.stream(classes).forEach(clazz -> register(condition, clazz));
+ }
+
void register(C condition, Class> clazz);
void register(C condition, String clazz);
diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java
index 39cdd1e81ed3..c0e9dbca21f8 100644
--- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java
+++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java
@@ -127,7 +127,7 @@ public void addResourceBundles(UnresolvedAccessCondition condition, String basen
}
@Override
- public void addCondition(AccessCondition configurationCondition, Module module, String resourcePath) {
+ public void addCondition(AccessCondition accessCondition, Module module, String resourcePath) {
}
diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java
index 213cc2d80652..1917bb172af3 100644
--- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java
+++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java
@@ -31,11 +31,12 @@
import java.util.regex.Pattern;
import org.graalvm.collections.EconomicMap;
+import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
-import org.graalvm.nativeimage.hosted.RuntimeReflection;
+import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
import org.graalvm.nativeimage.impl.TypeReachabilityCondition;
import com.oracle.svm.core.configure.ConditionalRuntimeValue;
@@ -145,14 +146,14 @@ private static Object createProxyClass(Class>[] interfaces) {
* InvocationHandler)`, is registered for reflection so that dynamic proxy instances can
* be allocated at run time.
*/
- RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));
/*
* The proxy class reflectively looks up the methods of the interfaces it implements to
* pass a Method object to InvocationHandler.
*/
for (Class> intf : interfaces) {
- RuntimeReflection.register(intf.getMethods());
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, intf.getMethods());
}
return clazz;
} catch (Throwable t) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/DynamicAccessSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/DynamicAccessSupport.java
new file mode 100644
index 000000000000..63c7e128a0d4
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/DynamicAccessSupport.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import com.oracle.svm.core.util.UserError;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.TypeReachabilityCondition;
+
+public final class DynamicAccessSupport {
+ private static boolean sealed = false;
+
+ static void setRegistrationSealed() {
+ sealed = true;
+ }
+
+ public static void printErrorIfSealedOrInvalidCondition(AccessCondition condition, String registrationEntry) {
+ if (condition == null) {
+ UserError.abort(new NullPointerException(), "Condition value must not be null. Please ensure that all values you register are not null.");
+ }
+ if (!(condition instanceof TypeReachabilityCondition)) {
+ UserError.abort(new IllegalStateException(), "Condition %s is not valid. Condition must be either alwaysTrue or typeReached. %s", condition);
+ }
+ if (sealed) {
+ UserError.abort(new IllegalStateException(), "Registration for runtime access after Feature#afterRegistration is not allowed. You tried to register %s", registrationEntry);
+ }
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java
index 9309d6d8ce53..5aef06dcc4b1 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureHandler.java
@@ -63,6 +63,7 @@
import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.options.Option;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
/**
* Handles the registration and iterations of {@link Feature features}.
@@ -90,11 +91,15 @@ private static List userEnabledFeatures() {
public void forEachFeature(Consumer consumer) {
for (Feature feature : featureInstances) {
try {
+ if (!ImageSingletons.lookup(APIDeprecationSupport.class).isUserEnabledFeaturesStarted() && Options.userEnabledFeatures().contains(feature.getClass().getName())) {
+ ImageSingletons.lookup(APIDeprecationSupport.class).setUserEnabledFeaturesStarted(true);
+ }
consumer.accept(feature);
} catch (Throwable t) {
throw handleFeatureError(feature, t);
}
}
+ ImageSingletons.lookup(APIDeprecationSupport.class).setUserEnabledFeaturesStarted(false);
}
public void forEachGraalFeature(Consumer consumer) {
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 40c22ce35565..6f1c9c52441c 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
@@ -48,10 +48,14 @@
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.AnnotationAccess;
-import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.dynamicaccess.ForeignAccess;
+import org.graalvm.nativeimage.dynamicaccess.JNIAccess;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
+import org.graalvm.nativeimage.dynamicaccess.ResourceAccess;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import com.oracle.graal.pointsto.BigBang;
@@ -196,6 +200,26 @@ public void setMainEntryPoint(Pair mainEntryPoint) {
public Pair getMainEntryPoint() {
return mainEntryPoint;
}
+
+ @Override
+ public ReflectiveAccess getReflectiveAccess() {
+ return ReflectiveAccessImpl.singleton();
+ }
+
+ @Override
+ public ResourceAccess getResourceAccess() {
+ return ResourceAccessImpl.singleton();
+ }
+
+ @Override
+ public JNIAccess getJNIAccess() {
+ return JNIAccessImpl.singleton();
+ }
+
+ @Override
+ public ForeignAccess getForeignAccess() {
+ return ForeignAccessImpl.singleton();
+ }
}
abstract static class AnalysisAccessBase extends FeatureAccessImpl {
@@ -586,7 +610,7 @@ public void registerOpaqueMethodReturn(Method method) {
* before/during analysis only in some builds when the initialization happened fast enough,
* resulting in unstable number of reachable methods and unstable decisions of the
* simulation of class initializers.
- *
+ *
* @see SVMHost#allowStableFieldFoldingBeforeAnalysis
*/
public void allowStableFieldFoldingBeforeAnalysis(Field field) {
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ForeignAccessImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ForeignAccessImpl.java
new file mode 100644
index 000000000000..0547daa3e5a7
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ForeignAccessImpl.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.oracle.svm.hosted;
+
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.RuntimeForeignAccessSupport;
+import org.graalvm.nativeimage.dynamicaccess.ForeignAccess;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Arrays;
+
+public final class ForeignAccessImpl implements ForeignAccess {
+
+ private final RuntimeForeignAccessSupport foreignInstance;
+ private static ForeignAccessImpl instance;
+
+ private ForeignAccessImpl() {
+ foreignInstance = ImageSingletons.lookup(RuntimeForeignAccessSupport.class);
+ }
+
+ public static ForeignAccessImpl singleton() {
+ if (instance == null) {
+ instance = new ForeignAccessImpl();
+ }
+ return instance;
+ }
+
+ @Override
+ public void registerForDowncall(AccessCondition condition, Object desc, Object... options) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition,
+ "following descriptor and options pair for downcalls into foreign code: " + String.valueOf(desc) + Arrays.toString(options));
+ foreignInstance.registerForDowncall(condition, desc, options);
+ }
+
+ @Override
+ public void registerForUpcall(AccessCondition condition, Object desc, Object... options) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following descriptor and options pair for upcalls from foreign code: " + String.valueOf(desc) + Arrays.toString(options));
+ foreignInstance.registerForUpcall(condition, desc, options);
+ }
+
+ @Override
+ public void registerForDirectUpcall(AccessCondition condition, MethodHandle target, Object desc, Object... options) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following method handle as a fast upcall target: " + target);
+ foreignInstance.registerForDirectUpcall(condition, target, desc, options);
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalReflectiveAccess.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalReflectiveAccess.java
new file mode 100644
index 000000000000..f6131991546e
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalReflectiveAccess.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+
+import com.oracle.svm.hosted.reflect.ReflectionDataBuilder;
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
+import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
+import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
+
+public final class InternalReflectiveAccess implements ReflectiveAccess {
+
+ private final ReflectionDataBuilder rrsInstance;
+ private static InternalReflectiveAccess instance;
+
+ private InternalReflectiveAccess() {
+ rrsInstance = (ReflectionDataBuilder) ImageSingletons.lookup(RuntimeReflectionSupport.class);
+ }
+
+ public static InternalReflectiveAccess singleton() {
+ if (instance == null) {
+ instance = new InternalReflectiveAccess();
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(AccessCondition condition, Class>... classes) {
+ for (Class> clazz : classes) {
+ rrsInstance.register(condition, clazz);
+ rrsInstance.registerClassMetadata(condition, clazz);
+ }
+ }
+
+ @Override
+ public void registerForUnsafeAllocation(AccessCondition condition, Class>... classes) {
+ rrsInstance.registerUnsafeAllocation(condition, classes);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Executable... executables) {
+ Class>[] uniqueDeclaringClasses = java.util.Arrays.stream(executables)
+ .map(Executable::getDeclaringClass)
+ .distinct()
+ .toArray(Class>[]::new);
+
+ register(condition, uniqueDeclaringClasses);
+ rrsInstance.register(condition, false, executables);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Field... fields) {
+ Class>[] uniqueDeclaringClasses = java.util.Arrays.stream(fields)
+ .map(Field::getDeclaringClass)
+ .distinct()
+ .toArray(Class>[]::new);
+ register(condition, uniqueDeclaringClasses);
+ rrsInstance.register(condition, false, fields);
+ }
+
+ @Override
+ public void registerForSerialization(AccessCondition condition, Class>... classes) {
+ RuntimeSerializationSupport.singleton().register(condition, classes);
+ for (Class> clazz : classes) {
+ rrsInstance.registerClassMetadata(condition, clazz);
+ }
+ }
+
+ @Override
+ public Class> registerProxy(AccessCondition condition, Class>... interfaces) {
+ Class> proxy = ImageSingletons.lookup(RuntimeProxyRegistrySupport.class).registerProxy(condition, interfaces);
+ register(condition, proxy);
+ return proxy;
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalResourceAccess.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalResourceAccess.java
new file mode 100644
index 000000000000..4a860ddc34ce
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/InternalResourceAccess.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import java.lang.reflect.Method;
+import java.util.Objects;
+import java.util.ResourceBundle;
+
+import com.oracle.svm.core.util.UserError;
+import com.oracle.svm.util.ReflectionUtil;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
+import org.graalvm.nativeimage.dynamicaccess.ResourceAccess;
+
+public final class InternalResourceAccess implements ResourceAccess {
+
+ private final RuntimeResourceSupport rrsInstance;
+ private static InternalResourceAccess instance;
+
+ private InternalResourceAccess() {
+ rrsInstance = RuntimeResourceSupport.singleton();
+ }
+
+ public static InternalResourceAccess singleton() {
+ if (instance == null) {
+ instance = new InternalResourceAccess();
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(AccessCondition condition, Module module, String pattern) {
+ Objects.requireNonNull(pattern, "Resource pattern cannot be null. Please ensure that all values you register are not null.");
+ if (pattern.replace("\\*", "").contains("*")) {
+ String moduleName = module == null ? null : module.getName();
+ rrsInstance.addGlob(condition, moduleName, pattern, "Registered from API");
+ } else {
+ rrsInstance.addResource(condition, module, pattern.replace("\\*", "*"), "Registered from API");
+ }
+ }
+
+ @Override
+ public void registerResourceBundle(AccessCondition condition, ResourceBundle... bundles) {
+ for (ResourceBundle bundle : bundles) {
+ Objects.requireNonNull(bundle, "ResourceBundle value cannot be null. Please ensure that all values you register are not null.");
+ var cache = ReflectionUtil.readField(ResourceBundle.class, "cacheKey", bundle);
+ if (cache == null) {
+ UserError.abort(new IllegalStateException(), "ResourceBundle instances must be obtained via ResourceBundle#getBundle.");
+ }
+
+ Method m = ReflectionUtil.lookupMethod(cache.getClass(), "getModule");
+ Module modul = ReflectionUtil.invokeMethod(m, cache);
+
+ Method m2 = ReflectionUtil.lookupMethod(cache.getClass(), "getName");
+ String name = ReflectionUtil.invokeMethod(m2, cache);
+
+ String finalBundleName = (modul != null && modul.isNamed()) ? modul.getName() + ":" + name : name;
+ rrsInstance.addResourceBundles(condition, finalBundleName);
+ }
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/JNIAccessImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/JNIAccessImpl.java
new file mode 100644
index 000000000000..85947726cf50
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/JNIAccessImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+import org.graalvm.nativeimage.ImageSingletons;
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
+import org.graalvm.nativeimage.dynamicaccess.JNIAccess;
+
+public final class JNIAccessImpl implements JNIAccess {
+
+ private final RuntimeJNIAccessSupport jniInstance;
+ private static JNIAccessImpl instance;
+
+ private JNIAccessImpl() {
+ jniInstance = ImageSingletons.lookup(RuntimeJNIAccessSupport.class);
+ }
+
+ public static JNIAccessImpl singleton() {
+ if (instance == null) {
+ instance = new JNIAccessImpl();
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(AccessCondition condition, Class>... classes) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following classes for JNI access: " + Arrays.toString(classes));
+ jniInstance.register(condition, classes);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Executable... methods) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following methods for JNI access: " + Arrays.toString(methods));
+ jniInstance.register(condition, false, methods);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Field... fields) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following fields for JNI access: " + Arrays.toString(fields));
+ jniInstance.register(condition, false, fields);
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
index caf7ff435e4d..4d02a47b804d 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java
@@ -53,6 +53,7 @@
import java.util.function.BooleanSupplier;
import java.util.function.Function;
+import com.oracle.svm.hosted.reflect.ReflectionFeature;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Pair;
import org.graalvm.nativeimage.ImageInfo;
@@ -71,6 +72,7 @@
import org.graalvm.nativeimage.c.struct.RawStructure;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.Feature.OnAnalysisExitAccess;
+import org.graalvm.nativeimage.impl.APIDeprecationSupport;
import org.graalvm.nativeimage.impl.AnnotationExtractor;
import org.graalvm.nativeimage.impl.CConstantValueSupport;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
@@ -977,8 +979,12 @@ protected void setupNativeImage(OptionValues options, Map feature.afterRegistration(access));
+ DynamicAccessSupport.setRegistrationSealed();
setDefaultLibCIfMissing();
if (!Pair. empty().equals(access.getMainEntryPoint())) {
setAndVerifyMainEntryPoint(access, entryPoints);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReflectiveAccessImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReflectiveAccessImpl.java
new file mode 100644
index 000000000000..e9be16ac1cb9
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReflectiveAccessImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.dynamicaccess.ReflectiveAccess;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public final class ReflectiveAccessImpl implements ReflectiveAccess {
+
+ private final InternalReflectiveAccess rdaInstance;
+ private static ReflectiveAccessImpl instance;
+
+ private ReflectiveAccessImpl() {
+ rdaInstance = InternalReflectiveAccess.singleton();
+ }
+
+ public static ReflectiveAccessImpl singleton() {
+ if (instance == null) {
+ instance = new ReflectiveAccessImpl();
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(AccessCondition condition, Class>... classes) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following classes for reflection: " + Arrays.toString(classes));
+ rdaInstance.register(condition, classes);
+ }
+
+ @Override
+ public void registerForUnsafeAllocation(AccessCondition condition, Class>... classes) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following classes for reflection and unsafe allocation: " + Arrays.toString(classes));
+ rdaInstance.registerForUnsafeAllocation(condition, classes);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Executable... executables) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following methods for reflection: " + Arrays.toString(executables));
+ rdaInstance.register(condition, executables);
+ }
+
+ @Override
+ public void register(AccessCondition condition, Field... fields) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following fields for reflection: " + Arrays.toString(fields));
+ rdaInstance.register(condition, fields);
+ }
+
+ @Override
+ public void registerForSerialization(AccessCondition condition, Class>... classes) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following classes for serialization: " + Arrays.toString(classes));
+ rdaInstance.registerForSerialization(condition, classes);
+ }
+
+ @Override
+ public Class> registerProxy(AccessCondition condition, Class>... interfaces) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, "following interfaces that define a dynamic proxy class: " + Arrays.toString(interfaces));
+ return rdaInstance.registerProxy(condition, interfaces);
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourceAccessImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourceAccessImpl.java
new file mode 100644
index 000000000000..cfa999de8c1b
--- /dev/null
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourceAccessImpl.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.svm.hosted;
+
+import org.graalvm.nativeimage.dynamicaccess.AccessCondition;
+import org.graalvm.nativeimage.dynamicaccess.ResourceAccess;
+
+import java.util.ResourceBundle;
+
+public final class ResourceAccessImpl implements ResourceAccess {
+
+ private final InternalResourceAccess rdaInstance;
+ private static ResourceAccessImpl instance;
+
+ private ResourceAccessImpl() {
+ rdaInstance = InternalResourceAccess.singleton();
+ }
+
+ public static ResourceAccessImpl singleton() {
+ if (instance == null) {
+ instance = new ResourceAccessImpl();
+ }
+ return instance;
+ }
+
+ @Override
+ public void register(AccessCondition condition, Module module, String pattern) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, pattern);
+ rdaInstance.register(condition, module, pattern);
+ }
+
+ @Override
+ public void registerResourceBundle(AccessCondition condition, ResourceBundle... bundles) {
+ for (ResourceBundle bundle : bundles) {
+ DynamicAccessSupport.printErrorIfSealedOrInvalidCondition(condition, bundle.getBaseBundleName());
+ rdaInstance.registerResourceBundle(condition, bundle);
+ }
+ }
+}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java
index a756e4ac4f7b..e2ecac1cba3f 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/JNIRegistryAdapter.java
@@ -140,6 +140,11 @@ public void registerAsSerializable(AccessCondition condition, Class> clazz) {
VMError.shouldNotReachHere("serializable cannot be set on JNI registrations");
}
+ @Override
+ public void registerAsJniAccessed(AccessCondition condition, Class> clazz) {
+ VMError.shouldNotReachHere("jniAccessible cannot be set on JNI registrations");
+ }
+
private static void ensureJniAccessible(boolean jniAccessible) {
VMError.guarantee(jniAccessible, "JNIRegistryAdapter can only be used for JNI queries");
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java
index fe5f6d70b43c..00ef3d9c4f20 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java
@@ -325,7 +325,7 @@ public boolean registerAllConstructors(AccessCondition condition, boolean querie
@Override
public void registerUnsafeAllocated(AccessCondition condition, Class> clazz) {
if (!clazz.isArray() && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
- registry.register(condition, true, clazz);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerUnsafeAllocation(condition, clazz);
/*
* Ignore otherwise as the implementation of allocateInstance will anyhow throw an
* exception.
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java
index e45c018b97cd..9d81534bb61a 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java
@@ -247,7 +247,9 @@ public static void registerPreservedClasses(BigBang bb, MetaAccessProvider origi
// if we register as unsafe allocated earlier there are build-time
// initialization errors
- reflection.register(always, !(c.isArray() || c.isInterface() || c.isPrimitive() || Modifier.isAbstract(c.getModifiers())), c);
+ if (!(c.isArray() || c.isInterface() || c.isPrimitive() || Modifier.isAbstract(c.getModifiers()))) {
+ reflection.registerUnsafeAllocation(always, c);
+ }
/* Register resource bundles */
if (BundleContentSubstitutedLocalizationSupport.isBundleSupported(c)) {
@@ -275,7 +277,7 @@ public static void registerPreservedClasses(BigBang bb, MetaAccessProvider origi
private static void registerType(RuntimeReflectionSupport reflection, Class> c) {
AccessCondition always = AccessCondition.unconditional();
- reflection.register(always, false, c);
+ reflection.register(always, c);
reflection.registerAllDeclaredFields(always, c);
reflection.registerAllDeclaredMethodsQuery(always, false, c);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java
index 202d317384ac..c887d0268f86 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java
@@ -302,7 +302,6 @@ public void duringSetup(DuringSetupAccess a) {
String reason = "All ResourceBundleControlProvider that are registered as services end up as objects in the image heap, and are therefore registered to be initialized at image build time";
ServiceLoader.load(ResourceBundleControlProvider.class).stream()
.forEach(provider -> ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime(provider.type(), reason));
-
}
/**
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java
index 8a766087fa49..b614b2cc08b5 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java
@@ -224,8 +224,7 @@ private final class JNIRuntimeAccessibilitySupportImpl extends ConditionalConfig
implements RuntimeJNIAccessSupport, LayeredImageSingleton {
@Override
- public void register(AccessCondition condition, boolean unsafeAllocated, Class> clazz) {
- assert !unsafeAllocated : "unsafeAllocated can be only set via Unsafe.allocateInstance, not via JNI.";
+ public void register(AccessCondition condition, Class> clazz) {
Objects.requireNonNull(clazz, () -> nullErrorMessage("class", "JNI access"));
abortIfSealed();
registerConditionalConfiguration(condition, _ -> newClasses.add(clazz));
@@ -257,7 +256,7 @@ private void registerFields(boolean finalIsWritable, Field[] fields) {
@Override
public void registerClassLookup(AccessCondition condition, String reflectionName) {
try {
- register(condition, false, Class.forName(reflectionName));
+ register(condition, Class.forName(reflectionName));
} catch (ClassNotFoundException e) {
String jniName = ClassNameSupport.reflectionNameToJNIName(reflectionName);
newNegativeClassLookups.add(jniName);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java
index 12083287a2ed..e791ddc789a9 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java
@@ -37,7 +37,6 @@
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.ALL_PERMITTED_SUBCLASSES_FLAG;
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.ALL_RECORD_COMPONENTS_FLAG;
import static com.oracle.svm.core.code.RuntimeMetadataDecoderImpl.ALL_SIGNERS_FLAG;
-import static com.oracle.svm.core.configure.ConfigurationFiles.Options.TreatAllTypeReachableConditionsAsTypeReached;
import static org.graalvm.nativeimage.dynamicaccess.AccessCondition.unconditional;
import java.lang.annotation.AnnotationFormatError;
@@ -79,7 +78,6 @@
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
-import org.graalvm.nativeimage.impl.TypeReachabilityCondition;
import com.oracle.graal.pointsto.ObjectScanner.ScanReason;
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
@@ -209,10 +207,10 @@ private boolean isQueryFlagSet(Class> clazz, int flag) {
}
@Override
- public void register(AccessCondition condition, boolean unsafeInstantiated, Class> clazz) {
+ public void register(AccessCondition condition, Class> clazz) {
Objects.requireNonNull(clazz, () -> nullErrorMessage("class", "reflection"));
runConditionalInAnalysisTask(condition, (cnd) -> {
- registerClass(cnd, clazz, unsafeInstantiated, true);
+ registerClass(cnd, clazz, true);
if (FutureDefaultsOptions.completeReflectionTypes()) {
registerClassMetadata(cnd, clazz);
}
@@ -221,7 +219,6 @@ public void register(AccessCondition condition, boolean unsafeInstantiated, Clas
@Override
public void registerAllClassesQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_CLASSES_FLAG);
try {
@@ -230,7 +227,7 @@ public void registerAllClassesQuery(AccessCondition condition, Class> clazz) {
/* Malformed inner classes can have no declaring class */
innerClasses.computeIfAbsent(innerClass.getDeclaringClass(), _ -> ConcurrentHashMap.newKeySet()).add(innerClass);
}
- registerClass(cnd, innerClass, false, !MissingRegistrationUtils.throwMissingRegistrationErrors());
+ registerClass(cnd, innerClass, !MissingRegistrationUtils.throwMissingRegistrationErrors());
}
} catch (LinkageError e) {
registerLinkageError(clazz, e, classLookupExceptions);
@@ -238,7 +235,7 @@ public void registerAllClassesQuery(AccessCondition condition, Class> clazz) {
});
}
- void registerClassMetadata(AccessCondition condition, Class> clazz) {
+ public void registerClassMetadata(AccessCondition condition, Class> clazz) {
registerAllDeclaredFieldsQuery(condition, true, clazz);
registerAllFieldsQuery(condition, true, clazz);
registerAllDeclaredMethodsQuery(condition, true, clazz);
@@ -253,28 +250,14 @@ void registerClassMetadata(AccessCondition condition, Class> clazz) {
registerAllSignersQuery(condition, clazz);
}
- /**
- * Runtime conditions can only be used with type, so they are not valid here.
- */
- @SuppressWarnings("unused")
- private static void guaranteeNotRuntimeConditionForQueries(AccessCondition cnd, boolean queriedOnly) {
- VMError.guarantee(cnd instanceof TypeReachabilityCondition, "Condition must be TypeReachabilityCondition.");
- TypeReachabilityCondition reachabilityCondition = (TypeReachabilityCondition) cnd;
- if (!TreatAllTypeReachableConditionsAsTypeReached.getValue()) {
- VMError.guarantee(!queriedOnly || reachabilityCondition.isAlwaysTrue() || !reachabilityCondition.isRuntimeChecked(),
- "Bulk queries can only be set with 'name' which does not allow run-time conditions.");
- }
- }
-
@Override
public void registerAllDeclaredClassesQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_DECLARED_CLASSES_FLAG);
try {
for (Class> innerClass : clazz.getDeclaredClasses()) {
innerClasses.computeIfAbsent(clazz, _ -> ConcurrentHashMap.newKeySet()).add(innerClass);
- registerClass(cnd, innerClass, false, !MissingRegistrationUtils.throwMissingRegistrationErrors());
+ registerClass(cnd, innerClass, !MissingRegistrationUtils.throwMissingRegistrationErrors());
}
} catch (LinkageError e) {
registerLinkageError(clazz, e, classLookupExceptions);
@@ -282,17 +265,29 @@ public void registerAllDeclaredClassesQuery(AccessCondition condition, Class>
});
}
- private void registerClass(AccessCondition condition, Class> clazz, boolean unsafeInstantiated, boolean allowForName) {
+ @Override
+ public void registerUnsafeAllocation(AccessCondition condition, Class>... classes) {
+ Objects.requireNonNull(classes);
+ runConditionalInAnalysisTask(condition, (cnd) -> {
+ for (Class> clazz : classes) {
+ if (shouldExcludeClass(clazz)) {
+ return;
+ }
+ AnalysisType type = metaAccess.lookupJavaType(clazz);
+ type.registerAsReachable("Is registered for unsafe allocation.");
+ type.registerAsUnsafeAllocated("Is registered via reflection metadata.");
+ classForNameSupport.registerUnsafeAllocated(cnd, clazz);
+ }
+ });
+ }
+
+ private void registerClass(AccessCondition condition, Class> clazz, boolean allowForName) {
if (shouldExcludeClass(clazz)) {
return;
}
AnalysisType type = metaAccess.lookupJavaType(clazz);
type.registerAsReachable("Is registered for reflection.");
- if (unsafeInstantiated) {
- type.registerAsUnsafeAllocated("Is registered via reflection metadata.");
- classForNameSupport.registerUnsafeAllocated(condition, clazz);
- }
if (allowForName) {
classForNameSupport.registerClass(condition, clazz, ClassLoaderFeature.getRuntimeClassLoader(clazz.getClassLoader()));
@@ -326,7 +321,7 @@ public void registerClassLookupException(AccessCondition condition, String typeN
public void registerClassLookup(AccessCondition condition, String typeName) {
runConditionalInAnalysisTask(condition, (cnd) -> {
try {
- registerClass(cnd, Class.forName(typeName, false, ClassLoader.getSystemClassLoader()), false, true);
+ registerClass(cnd, Class.forName(typeName, false, ClassLoader.getSystemClassLoader()), true);
} catch (ClassNotFoundException e) {
classForNameSupport.registerNegativeQuery(cnd, typeName);
} catch (Throwable t) {
@@ -337,7 +332,6 @@ public void registerClassLookup(AccessCondition condition, String typeName) {
@Override
public void registerAllRecordComponentsQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, _ -> {
setQueryFlag(clazz, ALL_RECORD_COMPONENTS_FLAG);
registerRecordComponents(clazz);
@@ -346,12 +340,11 @@ public void registerAllRecordComponentsQuery(AccessCondition condition, Class>
@Override
public void registerAllPermittedSubclassesQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
- runConditionalInAnalysisTask(condition, _ -> {
+ runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_PERMITTED_SUBCLASSES_FLAG);
if (clazz.isSealed()) {
for (Class> permittedSubclass : clazz.getPermittedSubclasses()) {
- registerClass(condition, permittedSubclass, false, false);
+ registerClass(cnd, permittedSubclass, false);
}
}
});
@@ -359,12 +352,11 @@ public void registerAllPermittedSubclassesQuery(AccessCondition condition, Class
@Override
public void registerAllNestMembersQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
- runConditionalInAnalysisTask(condition, _ -> {
+ runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_NEST_MEMBERS_FLAG);
for (Class> nestMember : clazz.getNestMembers()) {
if (nestMember != clazz) {
- registerClass(condition, nestMember, false, false);
+ registerClass(cnd, nestMember, false);
}
}
});
@@ -372,7 +364,6 @@ public void registerAllNestMembersQuery(AccessCondition condition, Class> claz
@Override
public void registerAllSignersQuery(AccessCondition condition, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, _ -> {
setQueryFlag(clazz, ALL_SIGNERS_FLAG);
Object[] signers = clazz.getSigners();
@@ -392,7 +383,6 @@ public void register(AccessCondition condition, boolean queriedOnly, Executable.
@Override
public void registerAllMethodsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, queriedOnly);
runConditionalInAnalysisTask(condition, (cnd) -> {
for (Class> current = clazz; current != null; current = current.getSuperclass()) {
setQueryFlag(current, ALL_METHODS_FLAG);
@@ -407,7 +397,6 @@ public void registerAllMethodsQuery(AccessCondition condition, boolean queriedOn
@Override
public void registerAllDeclaredMethodsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, queriedOnly);
runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_DECLARED_METHODS_FLAG);
try {
@@ -420,7 +409,6 @@ public void registerAllDeclaredMethodsQuery(AccessCondition condition, boolean q
@Override
public void registerAllConstructorsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, queriedOnly);
runConditionalInAnalysisTask(condition, (cnd) -> {
for (Class> current = clazz; current != null; current = current.getSuperclass()) {
setQueryFlag(current, ALL_CONSTRUCTORS_FLAG);
@@ -435,7 +423,6 @@ public void registerAllConstructorsQuery(AccessCondition condition, boolean quer
@Override
public void registerAllDeclaredConstructorsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, queriedOnly);
runConditionalInAnalysisTask(condition, (cnd) -> {
setQueryFlag(clazz, ALL_DECLARED_CONSTRUCTORS_FLAG);
try {
@@ -535,7 +522,6 @@ private void registerMethod(AccessCondition cnd, boolean queriedOnly, Executable
@Override
public void registerMethodLookup(AccessCondition condition, Class> declaringClass, String methodName, Class>... parameterTypes) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, (cnd) -> {
try {
registerMethod(cnd, true, declaringClass.getDeclaredMethod(methodName, parameterTypes));
@@ -550,7 +536,6 @@ public void registerMethodLookup(AccessCondition condition, Class> declaringCl
@Override
public void registerConstructorLookup(AccessCondition condition, Class> declaringClass, Class>... parameterTypes) {
- guaranteeNotRuntimeConditionForQueries(condition, true);
runConditionalInAnalysisTask(condition, (cnd) -> {
try {
registerMethod(cnd, true, declaringClass.getDeclaredConstructor(parameterTypes));
@@ -574,8 +559,8 @@ public void registerAllFields(AccessCondition condition, Class> clazz) {
registerAllFieldsQuery(condition, false, clazz);
}
+ @Override
public void registerAllFieldsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
- guaranteeNotRuntimeConditionForQueries(condition, queriedOnly);
runConditionalInAnalysisTask(condition, (cnd) -> {
for (Class> current = clazz; current != null; current = current.getSuperclass()) {
setQueryFlag(current, ALL_FIELDS_FLAG);
@@ -598,6 +583,7 @@ private record AllDeclaredFieldsQuery(AccessCondition condition, boolean queried
private Set existingAllDeclaredFieldsQuery = ConcurrentHashMap.newKeySet();
+ @Override
public void registerAllDeclaredFieldsQuery(AccessCondition condition, boolean queriedOnly, Class> clazz) {
final var query = new AllDeclaredFieldsQuery(condition, queriedOnly, clazz);
if (!existingAllDeclaredFieldsQuery.contains(query)) {
@@ -1109,7 +1095,7 @@ private void visitTypesForMemberValue(Object memberValue, Consumer> typ
}
case MissingType _ -> typeConsumer.accept(TypeNotPresentExceptionProxy.class);
case ElementTypeMismatch _ -> typeConsumer.accept(AnnotationTypeMismatchExceptionProxy);
- case AnnotationFormatError _, Number _, Boolean _, Character _ -> {
+ case AnnotationFormatError _,Number _,Boolean _,Character _ -> {
}
default -> throw GraalError.shouldNotReachHere("Invalid annotation member type: " + memberValue.getClass()); // ExcludeFromJacocoGeneratedReport
}
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java
index cee76243376e..ab6533c29930 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionFeature.java
@@ -36,6 +36,9 @@
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import com.oracle.svm.core.option.HostedOptionKey;
+import jdk.graal.compiler.options.Option;
+import jdk.graal.compiler.options.OptionType;
import org.graalvm.nativeimage.AnnotationAccess;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.c.function.CFunctionPointer;
@@ -145,6 +148,11 @@ private record AccessorKey(Executable member, Class> targetClass) {
FeatureImpl.BeforeAnalysisAccessImpl analysisAccess;
+ public static final class Options {
+ @Option(help = "Emits a warning when the old programmatic registration API is used.", type = OptionType.User) public static final HostedOptionKey TrackDeprecatedRegistrationUsage = new HostedOptionKey<>(
+ false);
+ }
+
@Override
public SubstrateAccessor getOrCreateAccessor(Executable member) {
return getOrCreateConstructorAccessor(member.getDeclaringClass(), member);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java
index cd578301b381..191e4162c7cb 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java
@@ -414,7 +414,7 @@ public void register(AccessCondition condition, Class> serializationTargetClas
* Register class for reflection as it is needed when the class-value itself is
* serialized.
*/
- ImageSingletons.lookup(RuntimeReflectionSupport.class).register(condition, serializationTargetClass);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, serializationTargetClass);
if (!Serializable.class.isAssignableFrom(serializationTargetClass)) {
return;
@@ -423,10 +423,10 @@ public void register(AccessCondition condition, Class> serializationTargetClas
* Making this class reachable as it will end up in the image heap without the analysis
* knowing.
*/
- RuntimeReflection.register(java.io.ObjectOutputStream.class);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, java.io.ObjectOutputStream.class);
if (denyRegistry.isAllowed(serializationTargetClass)) {
- addOrQueueConstructorAccessors(cnd, serializationTargetClass, getHostVM().dynamicHub(serializationTargetClass));
+ addOrQueueConstructorAccessors(cnd, serializationTargetClass);
Class> superclass = serializationTargetClass.getSuperclass();
if (superclass != null) {
@@ -442,13 +442,13 @@ public void register(AccessCondition condition, Class> serializationTargetClas
});
}
- private void addOrQueueConstructorAccessors(AccessCondition cnd, Class> serializationTargetClass, DynamicHub hub) {
+ private void addOrQueueConstructorAccessors(AccessCondition cnd, Class> serializationTargetClass) {
if (pendingConstructorRegistrations != null) {
// cannot yet create constructor accessor -> add to pending
- pendingConstructorRegistrations.add(() -> registerConstructorAccessors(cnd, serializationTargetClass, hub));
+ pendingConstructorRegistrations.add(() -> registerConstructorAccessors(cnd, serializationTargetClass, getHostVM().dynamicHub(serializationTargetClass)));
} else {
// can already run the registration
- registerConstructorAccessors(cnd, serializationTargetClass, hub);
+ registerConstructorAccessors(cnd, serializationTargetClass, getHostVM().dynamicHub(serializationTargetClass));
}
}
@@ -477,10 +477,10 @@ void beforeAnalysis(Feature.BeforeAnalysisAccess beforeAnalysisAccess) {
private static void registerQueriesForInheritableMethod(Class> clazz, String methodName, Class>... args) {
Class> iter = clazz;
while (iter != null) {
- RuntimeReflection.registerMethodLookup(iter, methodName, args);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), iter, methodName, args);
Method method = ReflectionUtil.lookupMethod(true, clazz, methodName, args);
if (method != null) {
- RuntimeReflection.register(method);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, method);
break;
}
iter = iter.getSuperclass();
@@ -492,7 +492,7 @@ private static void registerMethod(AccessCondition cnd, Class> clazz, String m
if (method != null) {
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, method);
} else {
- RuntimeReflection.registerMethodLookup(clazz, methodName, args);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerMethodLookup(AccessCondition.unconditional(), clazz, methodName, args);
}
}
@@ -513,7 +513,7 @@ private void registerForSerialization(AccessCondition cnd, Class> serializatio
boolean initClValid = true;
while (Serializable.class.isAssignableFrom(initCl)) {
Class> prev = initCl;
- RuntimeReflection.registerAllDeclaredConstructors(initCl);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(cnd, true, initCl);
if ((initCl = initCl.getSuperclass()) == null || (!disableSerialConstructorChecks() &&
!prev.isArray() && !superHasAccessibleConstructor(prev))) {
initClValid = false;
@@ -522,18 +522,18 @@ private void registerForSerialization(AccessCondition cnd, Class> serializatio
}
if (initClValid) {
- RuntimeReflection.registerAllDeclaredConstructors(initCl);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(cnd, true, initCl);
}
Class> iter = serializationTargetClass;
while (iter != null) {
- RuntimeReflection.registerAllDeclaredFields(iter);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(cnd, iter);
try {
Arrays.stream(iter.getDeclaredFields())
.map(Field::getType).forEach(type -> {
- RuntimeReflection.registerAllDeclaredMethods(type);
- RuntimeReflection.registerAllDeclaredFields(type);
- RuntimeReflection.registerAllDeclaredConstructors(type);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredMethodsQuery(AccessCondition.unconditional(), true, type);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(AccessCondition.unconditional(), type);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(AccessCondition.unconditional(), true, type);
});
} catch (LinkageError l) {
/* Handled with registration above */
@@ -551,20 +551,20 @@ private void registerForSerialization(AccessCondition cnd, Class> serializatio
@SuppressWarnings("unused")
static void registerSerializationUIDElements(Class> serializationTargetClass, boolean fullyRegister) {
- RuntimeReflection.registerAllDeclaredConstructors(serializationTargetClass);
- RuntimeReflection.registerAllDeclaredMethods(serializationTargetClass);
- RuntimeReflection.registerAllDeclaredFields(serializationTargetClass);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredConstructorsQuery(AccessCondition.unconditional(), true, serializationTargetClass);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredMethodsQuery(AccessCondition.unconditional(), true, serializationTargetClass);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllDeclaredFields(AccessCondition.unconditional(), serializationTargetClass);
if (fullyRegister) {
try {
/* This is here a legacy that we can't remove as it is a breaking change */
- RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
- RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
- RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, serializationTargetClass.getDeclaredConstructors());
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, serializationTargetClass.getDeclaredMethods());
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).register(AccessCondition.unconditional(), false, serializationTargetClass.getDeclaredFields());
} catch (LinkageError e) {
/* Handled by registrations above */
}
}
- RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerFieldLookup(AccessCondition.unconditional(), serializationTargetClass, "serialPersistentFields");
}
public void afterAnalysis() {
@@ -593,7 +593,7 @@ private static void registerForDeserialization(AccessCondition cnd, Class> ser
*/
}
} else if (Externalizable.class.isAssignableFrom(serializationTargetClass)) {
- RuntimeReflection.registerConstructorLookup(serializationTargetClass);
+ ImageSingletons.lookup(RuntimeReflectionSupport.class).registerConstructorLookup(AccessCondition.unconditional(), serializationTargetClass);
}
registerMethod(cnd, serializationTargetClass, "readObject", ObjectInputStream.class);
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/JSEntryPointRegistry.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/JSEntryPointRegistry.java
index 3690019bb39c..f1de368ca318 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/JSEntryPointRegistry.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/JSEntryPointRegistry.java
@@ -47,7 +47,7 @@ public class JSEntryPointRegistry implements ReflectionRegistry {
public final Set entryPoints = Collections.newSetFromMap(new ConcurrentHashMap<>());
@Override
- public void register(AccessCondition condition, boolean unsafeAllocated, Class> clazz) {
+ public void register(AccessCondition condition, Class> clazz) {
// Do nothing for types
}
diff --git a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageRuntimeJNIAccessSupport.java b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageRuntimeJNIAccessSupport.java
index 4df23938c536..809beca1e49f 100644
--- a/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageRuntimeJNIAccessSupport.java
+++ b/web-image/src/com.oracle.svm.hosted.webimage/src/com/oracle/svm/hosted/webimage/WebImageRuntimeJNIAccessSupport.java
@@ -40,7 +40,7 @@
*/
public class WebImageRuntimeJNIAccessSupport implements RuntimeJNIAccessSupport {
@Override
- public void register(AccessCondition condition, boolean unsafeAllocated, Class> clazz) {
+ public void register(AccessCondition condition, Class> clazz) {
// Do nothing.
}