Skip to content

Commit 47df6a9

Browse files
author
Mihailo Markovic
committed
Introduction of new API for metadata registration based on conditions.
1 parent 6e68bf8 commit 47df6a9

19 files changed

+470
-40
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.graalvm.nativeimage.hosted;
2+
3+
import java.lang.reflect.Executable;
4+
import java.lang.reflect.Field;
5+
6+
public interface DynamicJNIAccess {
7+
8+
/**
9+
* Registers the provided classes for both JNI access and reflection at runtime, if the
10+
* {@code condition} is satisfied.
11+
*/
12+
void register(RegistrationCondition condition, Class<?>... classes);
13+
14+
/**
15+
* Registers the provided methods for both JNI access and reflection at runtime, if the
16+
* {@code condition} is satisfied.
17+
* {@link ReflectionDynamicAccess#register(RegistrationCondition, Executable...)} is implicitly
18+
* called, thereby this method also registers the declaring classes of those methods, including
19+
* all their members, for runtime reflection queries.
20+
*/
21+
void register(RegistrationCondition condition, Executable... methods);
22+
23+
/**
24+
* Registers the provided fields for both JNI access and reflection at runtime, if the
25+
* {@code condition} is satisfied.
26+
* {@link ReflectionDynamicAccess#register(RegistrationCondition, Field...)} is implicitly
27+
* called, thereby this method also registers the declaring classes of those fields, including
28+
* all their members, for runtime reflection queries.
29+
*/
30+
void register(RegistrationCondition condition, Field... fields);
31+
}

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/Feature.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,24 @@ interface IsInConfigurationAccess extends FeatureAccess {
171171
*/
172172
@Platforms(Platform.HOSTED_ONLY.class)
173173
interface AfterRegistrationAccess extends FeatureAccess {
174+
175+
/**
176+
* Returns the access instance used to register elements for reflection at runtime. All
177+
* registrations should happen in {@link Feature#afterRegistration}.
178+
*/
179+
ReflectionDynamicAccess getReflectionDynamicAccess();
180+
181+
/**
182+
* Returns the access instance used to register resources for runtime access. All
183+
* registrations should happen in {@link Feature#afterRegistration}.
184+
*/
185+
ResourceDynamicAccess getResourceDynamicAccess();
186+
187+
/**
188+
* Returns the access instance used to register elements for JNI access at runtime. All
189+
* registrations should happen in {@link Feature#afterRegistration}.
190+
*/
191+
DynamicJNIAccess getDynamicJNIAccess();
174192
}
175193

176194
/**
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.graalvm.nativeimage.hosted;
2+
3+
import java.lang.reflect.Executable;
4+
import java.lang.reflect.Field;
5+
6+
/**
7+
* This interface is used to register classes, methods, fields for reflection, serialization, and
8+
* JNI access at runtime.
9+
*
10+
* All methods in {@link ReflectionDynamicAccess} require a {@link RegistrationCondition} as their
11+
* first parameter. A class and its members will be registered for dynamic access only if the
12+
* specified condition is satisfied.
13+
*
14+
* {@link ReflectionDynamicAccess} should only be used during {@link Feature#afterRegistration}. Any
15+
* attempt of registration in any other phase will result as an error.
16+
*/
17+
public interface ReflectionDynamicAccess {
18+
19+
/**
20+
* Registers the provided classes for reflection at runtime, and all of their accessible members
21+
* available for reflection queries at runtime, if the {@code condition} is satisfied. A call to
22+
* {@link Class#forName} for the names of the classes will return the classes at runtime.
23+
*
24+
* If the class is not registered for reflection at runtime, {@link Class#forName} will throw
25+
* {@link org.graalvm.nativeimage.MissingReflectionRegistrationError}.
26+
*/
27+
void register(RegistrationCondition condition, Class<?>... classes);
28+
29+
/**
30+
* If the {@code condition} is satisfied, this method registers the class with the provided name
31+
* for reflection at runtime.
32+
* <ul>
33+
* <li>If the class exists, a call to {@link Class#forName} for the name of the class will
34+
* return the class at runtime.</li>
35+
* <li>Otherwise, {@link Class#forName} will throw {@link ClassNotFoundException} at
36+
* runtime.</li>
37+
* </ul>
38+
*
39+
* If the class is not registered for reflection at runtime, {@link Class#forName} will throw
40+
* {@link org.graalvm.nativeimage.MissingReflectionRegistrationError}.
41+
*/
42+
void registerClassLookup(RegistrationCondition condition, String className);
43+
44+
/**
45+
* Registers the provided methods for reflective invocation at runtime, along with the declaring
46+
* classes of those methods, including all their members, for reflection queries at runtime, if
47+
* the {@code condition} is satisfied. The methods will be invocable via
48+
* {@link java.lang.reflect.Method#invoke(java.lang.Object, java.lang.Object...)}.
49+
*/
50+
void register(RegistrationCondition condition, Executable... methods);
51+
52+
/**
53+
* Registers the provided fields for reflective access at runtime, along with the declaring
54+
* classes of those fields, including all their members, for reflection queries at runtime, if
55+
* the {@code condition} is satisfied. The fields will be accessible via
56+
* {@link java.lang.reflect.Field#set(java.lang.Object, java.lang.Object)} and
57+
* {@link java.lang.reflect.Field#get(Object)}.
58+
*/
59+
void register(RegistrationCondition condition, Field... fields);
60+
61+
/**
62+
* Registers the provided classes for both serialization and reflection at runtime, if the
63+
* {@code condition} is satisfied.
64+
*/
65+
void registerForSerialization(RegistrationCondition condition, Class<?>... classes);
66+
67+
/**
68+
* Registers interfaces that defines {@link java.lang.reflect.Proxy} classes, if the
69+
* {@code condition} is satisfied. Proxy objects that match registered definition can be created
70+
* at runtime. The proxy class is fully defined by the interfaces it implements.
71+
*
72+
* @since 22.3
73+
*/
74+
void registerProxy(RegistrationCondition condition, Class<?>... interfaces);
75+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.graalvm.nativeimage.hosted;
2+
3+
/**
4+
* This interface is used to register Java resources and {@link java.util.ResourceBundle} that
5+
* should be accessible at runtime.
6+
*
7+
* All methods in {@link ResourceDynamicAccess} require a {@link RegistrationCondition} as their
8+
* first parameter. A class and its members will be registered for dynamic access only if the
9+
* specified condition is satisfied.
10+
*
11+
* {@link ResourceDynamicAccess} should only be used during {@link Feature#afterRegistration}. Any
12+
* attempt of registration in any other phase will result as an error.
13+
*/
14+
public interface ResourceDynamicAccess {
15+
16+
/**
17+
* Registers resources from the given {@code module} described with the provided {@code glob},
18+
* for dynamic access, if the {@code condition} is satisfied.
19+
*
20+
* @param condition represents the condition that needs to be satisfied in order to register
21+
* target resources.
22+
* @param module represents the Java module instance that contains target resources. If provided
23+
* value is {@code null}, resources are looked on classpath instead.
24+
* @param glob can either be exact Java resource path, or glob that contains wildcards such as
25+
* star(*) or globstar(**). Find glob syntax explanation <a href=
26+
* "/https://www.graalvm.org/latest/reference-manual/native-image/metadata/#resources">here</a>.
27+
*/
28+
void register(RegistrationCondition condition, Module module, String glob);
29+
30+
/**
31+
* Registers resources from classpath described with the provided {@code glob}, for dynamic
32+
* access, if the {@code condition} is satisfied.
33+
*
34+
* @param condition represents the condition that needs to be satisfied in order to register
35+
* target resources.
36+
* @param glob can either be exact Java resource path, or glob that contains wildcards such as
37+
* star(*) or globstar(**). Find glob syntax explanation <a href=
38+
* "/https://www.graalvm.org/latest/reference-manual/native-image/metadata/#resources">here</a>.
39+
*/
40+
default void register(RegistrationCondition condition, String glob) {
41+
register(condition, null, glob);
42+
}
43+
44+
/**
45+
* Registers {@link java.util.ResourceBundle} that is specified by a {@code bundleName} from
46+
* provided {@code module} for dynamic access, if the {@code condition} is satisfied. If the
47+
* given {@code module} value is {@code null}, the {@link java.util.ResourceBundle} is looked up
48+
* on the classpath instead.
49+
*/
50+
void registerResourceBundle(RegistrationCondition condition, Module module, String bundleName);
51+
52+
/**
53+
* Registers {@link java.util.ResourceBundle} that is specified by a {@code bundleName} from
54+
* classpath for dynamic access if the {@code condition} is satisfied.
55+
*/
56+
default void registerResourceBundle(RegistrationCondition condition, String bundleName) {
57+
registerResourceBundle(condition, null, bundleName);
58+
}
59+
}

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeResourceAccess.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,9 @@ public static void addResourceBundle(Module module, String bundleName) {
112112
}
113113

114114
private static String withModuleName(Module module, String str) {
115-
Objects.requireNonNull(module);
116115
Objects.requireNonNull(str);
117-
return (module.isNamed() ? module.getName() : "ALL-UNNAMED") + ":" + str;
116+
boolean isNamed = module != null && module.isNamed();
117+
return (isNamed) ? module.getName() + ":" + str : str;
118118
}
119119

120120
private RuntimeResourceAccess() {

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
5454

5555
void registerAllDeclaredMethodsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
5656

57+
void registerAllFieldsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
58+
59+
void registerAllDeclaredFieldsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
60+
5761
void registerAllConstructorsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
5862

5963
void registerAllDeclaredConstructorsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeSerializationSupport.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ static RuntimeSerializationSupport<RegistrationCondition> singleton() {
5656

5757
void registerIncludingAssociatedClasses(C condition, Class<?> clazz);
5858

59+
default void register(C condition, Class<?>... classes) {
60+
Arrays.stream(classes).forEach(clazz -> register(condition, clazz));
61+
}
62+
5963
void register(C condition, Class<?> clazz);
6064

6165
void register(C condition, String clazz);

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,7 @@ protected void registerConditionalConfiguration(RegistrationCondition condition,
6969
} else {
7070
beforeAnalysisAccess.registerReachabilityHandler(access -> consumer.accept(runtimeCondition), ((TypeReachabilityCondition) condition).getKey());
7171
}
72-
7372
}
74-
7573
}
7674

7775
public void setAnalysisAccess(Feature.BeforeAnalysisAccess beforeAnalysisAccess) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.oracle.svm.hosted;
2+
3+
import com.oracle.svm.core.util.UserError;
4+
5+
public final class DynamicAccessSupport {
6+
private static boolean afterRegistrationFinished = false;
7+
8+
static void setAfterRegistrationFinished() {
9+
afterRegistrationFinished = true;
10+
}
11+
12+
public static void printUserError(String registrationEntry) {
13+
UserError.guarantee(!afterRegistrationFinished, "Registration for runtime access after Feature#afterRegistration is not allowed. You tried to register %s", registrationEntry);
14+
}
15+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.oracle.svm.hosted;
2+
3+
import java.lang.reflect.Executable;
4+
import java.lang.reflect.Field;
5+
import java.util.Arrays;
6+
7+
import org.graalvm.nativeimage.ImageSingletons;
8+
import org.graalvm.nativeimage.hosted.DynamicJNIAccess;
9+
import org.graalvm.nativeimage.hosted.RegistrationCondition;
10+
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
11+
12+
public class DynamicJNIAccessImpl implements DynamicJNIAccess {
13+
14+
private InternalReflectionDynamicAccess rdaInstance;
15+
private RuntimeJNIAccessSupport rjaInstance;
16+
17+
DynamicJNIAccessImpl() {
18+
rdaInstance = new InternalReflectionDynamicAccess();
19+
rjaInstance = ImageSingletons.lookup(RuntimeJNIAccessSupport.class);
20+
}
21+
22+
@Override
23+
public void register(RegistrationCondition condition, Class<?>... classes) {
24+
DynamicAccessSupport.printUserError(Arrays.toString(classes));
25+
rdaInstance.register(condition, classes);
26+
rjaInstance.register(condition, classes);
27+
}
28+
29+
@Override
30+
public void register(RegistrationCondition condition, Executable... methods) {
31+
DynamicAccessSupport.printUserError(Arrays.toString(methods));
32+
rdaInstance.register(condition, methods);
33+
rjaInstance.register(condition, false, methods);
34+
}
35+
36+
@Override
37+
public void register(RegistrationCondition condition, Field... fields) {
38+
DynamicAccessSupport.printUserError(Arrays.toString(fields));
39+
rdaInstance.register(condition, fields);
40+
rjaInstance.register(condition, false, fields);
41+
}
42+
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,13 @@
4747

4848
import org.graalvm.collections.Pair;
4949
import org.graalvm.nativeimage.AnnotationAccess;
50+
import org.graalvm.nativeimage.hosted.DynamicJNIAccess;
5051
import org.graalvm.nativeimage.hosted.Feature;
5152
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
5253
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
54+
import org.graalvm.nativeimage.hosted.ReflectionDynamicAccess;
5355
import org.graalvm.nativeimage.hosted.RegistrationCondition;
56+
import org.graalvm.nativeimage.hosted.ResourceDynamicAccess;
5457
import org.graalvm.nativeimage.hosted.RuntimeReflection;
5558

5659
import com.oracle.graal.pointsto.BigBang;
@@ -192,6 +195,21 @@ public void setMainEntryPoint(Pair<Method, CEntryPointData> mainEntryPoint) {
192195
public Pair<Method, CEntryPointData> getMainEntryPoint() {
193196
return mainEntryPoint;
194197
}
198+
199+
@Override
200+
public ReflectionDynamicAccess getReflectionDynamicAccess() {
201+
return new ReflectionDynamicAccessImpl();
202+
}
203+
204+
@Override
205+
public ResourceDynamicAccess getResourceDynamicAccess() {
206+
return new ResourceDynamicAccessImpl();
207+
}
208+
209+
@Override
210+
public DynamicJNIAccess getDynamicJNIAccess() {
211+
return new DynamicJNIAccessImpl();
212+
}
195213
}
196214

197215
abstract static class AnalysisAccessBase extends FeatureAccessImpl {

0 commit comments

Comments
 (0)