Skip to content

Commit f95421a

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

16 files changed

+411
-40
lines changed

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

+8
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ interface IsInConfigurationAccess extends FeatureAccess {
171171
*/
172172
@Platforms(Platform.HOSTED_ONLY.class)
173173
interface AfterRegistrationAccess extends FeatureAccess {
174+
/**
175+
* Creates access for runtime registration. All registrations should happen in
176+
* {@link Feature#afterRegistration}
177+
*
178+
*/
179+
ReflectionDynamicAccess createReflectionDynamicAccess();
180+
181+
ResourceDynamicAccess createResourceDynamicAccess();
174182
}
175183

176184
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
* {@link ReflectionDynamicAccess} is based on {@link RegistrationCondition}. Registration of any
11+
* class and its members for dynamic access will succeed only if condition is met.
12+
*
13+
* {@link ReflectionDynamicAccess} should only be used at {@link Feature#afterRegistration}.
14+
*/
15+
public interface ReflectionDynamicAccess {
16+
17+
/**
18+
* Makes the provided classes available for reflection at runtime, and all of their accessible
19+
* members available for reflection queries at run time if {@code condition} is satisfied. A
20+
* call to {@link Class#forName} for the names of the classes will return the classes at run
21+
* time.
22+
*
23+
* @param condition needs to be satisfied for inclusion of types for reflection at runtime
24+
*/
25+
void register(RegistrationCondition condition, Class<?>... classes);
26+
27+
/**
28+
* Makes the provided class available for reflection at runtime if {@code condition} is
29+
* satisfied. A call to {@link Class#forName} for the name of the class will return the class
30+
* (if it exists) or a {@link ClassNotFoundException} at run time.
31+
*/
32+
void registerClassLookup(RegistrationCondition condition, String className);
33+
34+
/**
35+
* Makes the provided methods available for reflection at runtime if {@code condition} is
36+
* satisfied. The methods will be returned by {@link Class#getMethod},
37+
* {@link Class#getDeclaredMethod(String, Class[])}, and all the other methods on {@link Class}
38+
* that return a single method. Methods can be invoked reflectively.
39+
*/
40+
void register(RegistrationCondition condition, Executable... methods);
41+
42+
/**
43+
* Makes the provided fields available for reflection at runtime if {@code condition} is
44+
* satisfied. The fields will be returned by {@link Class#getField},
45+
* {@link Class#getDeclaredField(String)}, and all the other methods on {@link Class} that
46+
* return a single field. The fields can be accessed reflectively.
47+
*/
48+
void register(RegistrationCondition condition, Field... fields);
49+
50+
/**
51+
* Makes the provided classes available for both serialization and reflection at runtime if
52+
* {@code condition} is satisfied.
53+
*/
54+
void registerForSerialization(RegistrationCondition condition, Class<?>... classes);
55+
56+
/**
57+
* Makes the provided classes available for both JNI access and reflection at runtime if
58+
* {@code condition} is satisfied.
59+
*/
60+
void registerForJNIAccess(RegistrationCondition condition, Class<?>... classes);
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.graalvm.nativeimage.hosted;
2+
3+
/**
4+
* This interface is used to register Java resources and ResourceBundles that should be accessible
5+
* at runtime.
6+
*
7+
* {@link ResourceDynamicAccess} is based on {@link RegistrationCondition}. Registration of any
8+
* resource for dynamic access will succeed only if condition is met.
9+
*
10+
* {@link ResourceDynamicAccess} should only be used at {@link Feature#afterRegistration}.
11+
*/
12+
public interface ResourceDynamicAccess {
13+
14+
/**
15+
* If {@code pattern} contains any wildcard patterns, such as star(*) or globstar(**), pattern
16+
* is treated as glob pattern from {@code module} and it will be registered if {@code condition}
17+
* is satisfied. All resources that match the glob are available at runtime. Otherwise, pattern
18+
* represents Java resource from {@code module} that will be available at run time if
19+
* {@code condition} is satisfied. If the given {@code module} is omitted or null, Java resource
20+
* is looked up on the classpath instead.
21+
*/
22+
void register(RegistrationCondition condition, Module module, String pattern);
23+
24+
default void register(RegistrationCondition condition, String pattern) {
25+
register(condition, null, pattern);
26+
}
27+
28+
/**
29+
* Makes Java ResourceBundle that is specified by a {@code bundleName} from module
30+
* {@code module} available at run time if {@code condition} is satisfied. If the given
31+
* {@code module} is omitted or null, the ResourceBundle is looked up on the classpath instead.
32+
*/
33+
void registerResourceBundle(RegistrationCondition condition, Module module, String bundleName);
34+
35+
default void registerResourceBundle(RegistrationCondition condition, String bundleName) {
36+
registerResourceBundle(condition, null, bundleName);
37+
}
38+
}

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

+4
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

+4
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

-2
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) {

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

+12
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@
5151
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
5252
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
5353
import org.graalvm.nativeimage.hosted.RegistrationCondition;
54+
import org.graalvm.nativeimage.hosted.ReflectionDynamicAccess;
55+
import org.graalvm.nativeimage.hosted.ResourceDynamicAccess;
5456
import org.graalvm.nativeimage.hosted.RuntimeReflection;
5557

5658
import com.oracle.graal.pointsto.BigBang;
@@ -192,6 +194,16 @@ public void setMainEntryPoint(Pair<Method, CEntryPointData> mainEntryPoint) {
192194
public Pair<Method, CEntryPointData> getMainEntryPoint() {
193195
return mainEntryPoint;
194196
}
197+
198+
@Override
199+
public ReflectionDynamicAccess createReflectionDynamicAccess() {
200+
return new ReflectionDynamicAccessImpl();
201+
}
202+
203+
@Override
204+
public ResourceDynamicAccess createResourceDynamicAccess() {
205+
return new ResourceDynamicAccessImpl();
206+
}
195207
}
196208

197209
abstract static class AnalysisAccessBase extends FeatureAccessImpl {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.oracle.svm.hosted;
2+
3+
import java.lang.reflect.Executable;
4+
import java.lang.reflect.Field;
5+
6+
import org.graalvm.nativeimage.ImageSingletons;
7+
import org.graalvm.nativeimage.hosted.ReflectionDynamicAccess;
8+
import org.graalvm.nativeimage.hosted.RegistrationCondition;
9+
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
10+
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
11+
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
12+
13+
public class InternalReflectionDynamicAccessImpl implements ReflectionDynamicAccess {
14+
15+
private static RuntimeReflectionSupport rrsInstance;
16+
17+
InternalReflectionDynamicAccessImpl() {
18+
rrsInstance = ImageSingletons.lookup(RuntimeReflectionSupport.class);
19+
}
20+
21+
@Override
22+
public void register(RegistrationCondition condition, Class<?>... classes) {
23+
rrsInstance.register(condition, classes);
24+
for (Class<?> clazz : classes) {
25+
rrsInstance.registerAllClassesQuery(condition, clazz);
26+
rrsInstance.registerAllDeclaredClassesQuery(condition, clazz);
27+
rrsInstance.registerAllDeclaredMethodsQuery(condition, true, clazz);
28+
rrsInstance.registerAllMethodsQuery(condition, true, clazz);
29+
rrsInstance.registerAllDeclaredConstructorsQuery(condition, true, clazz);
30+
rrsInstance.registerAllConstructorsQuery(condition, true, clazz);
31+
rrsInstance.registerAllFieldsQuery(condition, true, clazz);
32+
rrsInstance.registerAllDeclaredFieldsQuery(condition, true, clazz);
33+
rrsInstance.registerAllNestMembersQuery(condition, clazz);
34+
rrsInstance.registerAllPermittedSubclassesQuery(condition, clazz);
35+
rrsInstance.registerAllRecordComponentsQuery(condition, clazz);
36+
rrsInstance.registerAllSignersQuery(condition, clazz);
37+
}
38+
}
39+
40+
@Override
41+
public void registerClassLookup(RegistrationCondition condition, String className) {
42+
rrsInstance.registerClassLookup(condition, className);
43+
}
44+
45+
@Override
46+
public void register(RegistrationCondition condition, Executable... methods) {
47+
rrsInstance.register(condition, false, methods);
48+
}
49+
50+
@Override
51+
public void register(RegistrationCondition condition, Field... fields) {
52+
rrsInstance.register(condition, false, fields);
53+
}
54+
55+
@Override
56+
public void registerForSerialization(RegistrationCondition condition, Class<?>... classes) {
57+
register(condition, classes);
58+
RuntimeSerializationSupport.singleton().register(condition, classes);
59+
}
60+
61+
@Override
62+
public void registerForJNIAccess(RegistrationCondition condition, Class<?>... classes) {
63+
register(condition, classes);
64+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(condition, classes);
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.oracle.svm.hosted;
2+
3+
import java.util.Objects;
4+
5+
import org.graalvm.nativeimage.hosted.RegistrationCondition;
6+
import org.graalvm.nativeimage.hosted.ResourceDynamicAccess;
7+
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
8+
9+
public class InternalResourceDynamicAccessImpl implements ResourceDynamicAccess {
10+
11+
private static RuntimeResourceSupport<RegistrationCondition> rrsInstance;
12+
13+
InternalResourceDynamicAccessImpl() {
14+
rrsInstance = RuntimeResourceSupport.singleton();
15+
}
16+
17+
@Override
18+
public void register(RegistrationCondition condition, Module module, String pattern) {
19+
if (pattern.replace("\\*", "").contains("*")) {
20+
String moduleName = module == null ? null : module.getName();
21+
rrsInstance.addGlob(condition, moduleName, pattern, "Registered from API");
22+
} else {
23+
rrsInstance.addResource(condition, module, pattern.replace("\\*", "*"), "Registered from API");
24+
}
25+
}
26+
27+
@Override
28+
public void registerResourceBundle(RegistrationCondition condition, Module module, String bundleName) {
29+
rrsInstance.addResourceBundles(condition, resolveModuleName(module, bundleName));
30+
}
31+
32+
private static String resolveModuleName(Module module, String str) {
33+
Objects.requireNonNull(str);
34+
boolean isNamed = module == null ? false : module.isNamed();
35+
return ((isNamed) ? module.getName() : "ALL-UNNAMED") + ":" + str;
36+
}
37+
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,8 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
978978

979979
AfterRegistrationAccessImpl access = new AfterRegistrationAccessImpl(featureHandler, loader, originalMetaAccess, mainEntryPoint, debug);
980980
featureHandler.forEachFeature(feature -> feature.afterRegistration(access));
981+
ReflectionDynamicAccessImpl.setAfterRegistrationFinished();
982+
ResourceDynamicAccessImpl.setAfterRegistrationFinished();
981983
setDefaultLibCIfMissing();
982984
if (!Pair.<Method, CEntryPointData> empty().equals(access.getMainEntryPoint())) {
983985
setAndVerifyMainEntryPoint(access, entryPoints);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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.hosted.Feature;
8+
import org.graalvm.nativeimage.hosted.ReflectionDynamicAccess;
9+
import org.graalvm.nativeimage.hosted.RegistrationCondition;
10+
11+
import com.oracle.svm.core.util.UserError;
12+
13+
/**
14+
* Instance of this class is used to register classes, methods, and fields for reflection,
15+
* serialization and JNI access at runtime. It can only be created at
16+
* {@link Feature#afterRegistration} via {@link Feature.AfterRegistrationAccess}.
17+
*/
18+
public final class ReflectionDynamicAccessImpl implements ReflectionDynamicAccess {
19+
20+
private static boolean afterRegistrationFinished;
21+
private static InternalReflectionDynamicAccessImpl rdaInstance;
22+
23+
public ReflectionDynamicAccessImpl() {
24+
rdaInstance = new InternalReflectionDynamicAccessImpl();
25+
afterRegistrationFinished = false;
26+
}
27+
28+
public static void setAfterRegistrationFinished() {
29+
afterRegistrationFinished = true;
30+
}
31+
32+
@Override
33+
public void register(RegistrationCondition condition, Class<?>... classes) {
34+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
35+
Arrays.toString(classes));
36+
rdaInstance.register(condition, classes);
37+
}
38+
39+
@Override
40+
public void registerClassLookup(RegistrationCondition condition, String className) {
41+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
42+
className);
43+
rdaInstance.registerClassLookup(condition, className);
44+
}
45+
46+
@Override
47+
public void register(RegistrationCondition condition, Executable... methods) {
48+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
49+
Arrays.toString(methods));
50+
rdaInstance.register(condition, methods);
51+
}
52+
53+
@Override
54+
public void register(RegistrationCondition condition, Field... fields) {
55+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
56+
Arrays.toString(fields));
57+
rdaInstance.register(condition, fields);
58+
}
59+
60+
@Override
61+
public void registerForSerialization(RegistrationCondition condition, Class<?>... classes) {
62+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
63+
Arrays.toString(classes));
64+
rdaInstance.registerForSerialization(condition, classes);
65+
}
66+
67+
@Override
68+
public void registerForJNIAccess(RegistrationCondition condition, Class<?>... classes) {
69+
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
70+
Arrays.toString(classes));
71+
rdaInstance.registerForJNIAccess(condition, classes);
72+
}
73+
}

0 commit comments

Comments
 (0)