Skip to content

Commit c149086

Browse files
author
Mihailo Markovic
committed
Introduction of new API for metadata registration based on conditions.
1 parent 21ae240 commit c149086

19 files changed

+495
-36
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 JNI access at runtime.
8+
*
9+
* All methods in {@link DynamicJNIAccess} require a {@link RegistrationCondition} as their first
10+
* parameter. A class and its members will be registered for dynamic access only if the specified
11+
* condition is satisfied.
12+
*
13+
* {@link DynamicJNIAccess} should only be used during {@link Feature#afterRegistration}. Any
14+
* attempt of registration in any other phase will result in an error.
15+
*/
16+
public interface DynamicJNIAccess {
17+
18+
/**
19+
* Registers the provided classes for JNI access at runtime, if the {@code condition} is
20+
* satisfied.
21+
*/
22+
void register(RegistrationCondition condition, Class<?>... classes);
23+
24+
/**
25+
* Registers the provided methods for JNI access at runtime, if the {@code condition} is
26+
* satisfied.
27+
*/
28+
void register(RegistrationCondition condition, Executable... methods);
29+
30+
/**
31+
* Registers the provided fields for JNI access at runtime, if the {@code condition} is
32+
* satisfied.
33+
*/
34+
void register(RegistrationCondition condition, Field... fields);
35+
}

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: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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, and proxy definitions for
8+
* reflection, serialization, and proxy creation 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 in 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 a 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 a 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 classes for reflection and unsafe allocation at runtime, and all of
46+
* their accessible members available for reflection queries at runtime, if the
47+
* {@code condition} is satisfied.
48+
*
49+
* If a class is not registered for reflection at runtime, {@link Class#forName} will throw
50+
* {@link org.graalvm.nativeimage.MissingReflectionRegistrationError}.
51+
*/
52+
void registerUnsafeAllocated(RegistrationCondition condition, Class<?>... classes);
53+
54+
/**
55+
* Registers the provided methods for reflective invocation at runtime, along with the declaring
56+
* classes of those methods, including all their members, for reflection queries at runtime, if
57+
* the {@code condition} is satisfied. The methods will be invocable via
58+
* {@link java.lang.reflect.Method#invoke(java.lang.Object, java.lang.Object...)}.
59+
*/
60+
void register(RegistrationCondition condition, Executable... methods);
61+
62+
/**
63+
* Registers the provided fields for reflective access at runtime, along with the declaring
64+
* classes of those fields, including all their members, for reflection queries at runtime, if
65+
* the {@code condition} is satisfied. The fields will be accessible via
66+
* {@link java.lang.reflect.Field#set(java.lang.Object, java.lang.Object)} and
67+
* {@link java.lang.reflect.Field#get(Object)}.
68+
*/
69+
void register(RegistrationCondition condition, Field... fields);
70+
71+
/**
72+
* Registers the provided classes for both serialization and reflection at runtime, if the
73+
* {@code condition} is satisfied.
74+
*/
75+
void registerForSerialization(RegistrationCondition condition, Class<?>... classes);
76+
77+
/**
78+
* Registers interfaces that define {@link java.lang.reflect.Proxy} classes, if the
79+
* {@code condition} is satisfied. Proxy objects that match the registered definition can be
80+
* created at runtime. The proxy class is fully defined by the interfaces it implements.
81+
*
82+
* @return Proxy class defined by the provided interfaces
83+
*/
84+
Class<?> registerProxy(RegistrationCondition condition, Class<?>... interfaces);
85+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 in 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 the
23+
* provided value is {@code null} or an unnamed module, resources are looked up on
24+
* the classpath instead.
25+
* @param glob can either be exact Java resource path, or a glob that contains wildcards such as
26+
* star(*) or globstar(**). Find glob syntax explanation <a href=
27+
* "/https://www.graalvm.org/latest/reference-manual/native-image/metadata/#resources">here</a>.
28+
*/
29+
void register(RegistrationCondition condition, Module module, String glob);
30+
31+
/**
32+
* Registers resources from the classpath described with the provided {@code glob}, for dynamic
33+
* access, if the {@code condition} is satisfied.
34+
*
35+
* @param condition represents the condition that needs to be satisfied in order to register
36+
* target resources.
37+
* @param glob can either be exact Java resource path, or glob that contains wildcards such as
38+
* star(*) or globstar(**). Find glob syntax explanation <a href=
39+
* "/https://www.graalvm.org/latest/reference-manual/native-image/metadata/#resources">here</a>.
40+
*/
41+
default void register(RegistrationCondition condition, String glob) {
42+
register(condition, null, glob);
43+
}
44+
45+
/**
46+
* Registers {@link java.util.ResourceBundle} that is specified by a {@code bundleName} from the
47+
* provided {@code module} for dynamic access, if the {@code condition} is satisfied. If the
48+
* given {@code module} is unnamed or the value is {@code null}, the
49+
* {@link java.util.ResourceBundle} is looked up on the classpath instead.
50+
*/
51+
void registerResourceBundle(RegistrationCondition condition, Module module, String bundleName);
52+
53+
/**
54+
* Registers {@link java.util.ResourceBundle} that is specified by a {@code bundleName} from the
55+
* classpath for dynamic access if the {@code condition} is satisfied.
56+
*/
57+
default void registerResourceBundle(RegistrationCondition condition, String bundleName) {
58+
registerResourceBundle(condition, null, bundleName);
59+
}
60+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@
4444

4545
public interface RuntimeProxyCreationSupport {
4646
void addProxyClass(RegistrationCondition condition, Class<?>... interfaces);
47+
48+
Class<?> registerProxyClass(RegistrationCondition condition, Class<?>... interfaces);
4749
}

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
@@ -48,6 +48,10 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
4848

4949
void registerAllDeclaredMethodsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
5050

51+
void registerAllFieldsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
52+
53+
void registerAllDeclaredFieldsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
54+
5155
void registerAllConstructorsQuery(RegistrationCondition condition, boolean queriedOnly, Class<?> clazz);
5256

5357
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.core/src/com/oracle/svm/core/reflect/proxy/DynamicProxySupport.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ public synchronized void addProxyClass(RegistrationCondition condition, Class<?>
121121
proxyCache.get(key).getConditions().addCondition(condition);
122122
}
123123

124+
@Override
125+
public Class<?> registerProxyClass(RegistrationCondition condition, Class<?>... interfaces) {
126+
addProxyClass(condition, interfaces);
127+
return createProxyClassFromImplementedInterfaces(interfaces);
128+
}
129+
124130
@Platforms(Platform.HOSTED_ONLY.class)
125131
private static Object createProxyClass(Class<?>[] interfaces) {
126132
try {
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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
@Override
15+
public void register(RegistrationCondition condition, Class<?>... classes) {
16+
DynamicAccessSupport.printUserError("following classes for JNI access: " + Arrays.toString(classes));
17+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(condition, classes);
18+
}
19+
20+
@Override
21+
public void register(RegistrationCondition condition, Executable... methods) {
22+
DynamicAccessSupport.printUserError("following methods for JNI access: " + Arrays.toString(methods));
23+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(condition, false, methods);
24+
}
25+
26+
@Override
27+
public void register(RegistrationCondition condition, Field... fields) {
28+
DynamicAccessSupport.printUserError("following fields for JNI access: " + Arrays.toString(fields));
29+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(condition, false, fields);
30+
}
31+
}

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)