Skip to content

Commit 6ec749e

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

16 files changed

+439
-40
lines changed

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

Lines changed: 8 additions & 0 deletions
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 getReflectionDynamicAccess();
180+
181+
ResourceDynamicAccess getResourceDynamicAccess();
174182
}
175183

176184
/**
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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, if the {@code condition}
46+
* is satisfied. The methods will be invocable via
47+
* {@link java.lang.reflect.Method#invoke(java.lang.Object, java.lang.Object...)}.
48+
*/
49+
void register(RegistrationCondition condition, Executable... methods);
50+
51+
/**
52+
* Registers the provided fields for reflective access at runtime, if the {@code condition} is
53+
* satisfied. The fields will be accessible via
54+
* {@link java.lang.reflect.Field#set(java.lang.Object, java.lang.Object)} and
55+
* {@link java.lang.reflect.Field#get(Object)}.
56+
*/
57+
void register(RegistrationCondition condition, Field... fields);
58+
59+
/**
60+
* Registers the provided classes for both serialization and reflection at runtime, if the
61+
* {@code condition} is satisfied.
62+
*/
63+
void registerForSerialization(RegistrationCondition condition, Class<?>... classes);
64+
65+
/**
66+
* Registers the provided classes for both JNI access and reflection at runtime, if the
67+
* {@code condition} is satisfied.
68+
*/
69+
void registerForJNIAccess(RegistrationCondition condition, Class<?>... classes);
70+
}
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/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) {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
import org.graalvm.nativeimage.hosted.Feature;
5151
import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;
5252
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
53+
import org.graalvm.nativeimage.hosted.ReflectionDynamicAccess;
5354
import org.graalvm.nativeimage.hosted.RegistrationCondition;
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 getReflectionDynamicAccess() {
200+
return new ReflectionDynamicAccessImpl();
201+
}
202+
203+
@Override
204+
public ResourceDynamicAccess getResourceDynamicAccess() {
205+
return new ResourceDynamicAccessImpl();
206+
}
195207
}
196208

197209
abstract static class AnalysisAccessBase extends FeatureAccessImpl {
Lines changed: 66 additions & 0 deletions
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 InternalReflectionDynamicAccess implements ReflectionDynamicAccess {
14+
15+
private static RuntimeReflectionSupport rrsInstance;
16+
17+
InternalReflectionDynamicAccess() {
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+
}
Lines changed: 37 additions & 0 deletions
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 InternalResourceDynamicAccess implements ResourceDynamicAccess {
10+
11+
private static RuntimeResourceSupport<RegistrationCondition> rrsInstance;
12+
13+
InternalResourceDynamicAccess() {
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

Lines changed: 2 additions & 0 deletions
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);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 InternalReflectionDynamicAccess rdaInstance;
22+
23+
public ReflectionDynamicAccessImpl() {
24+
rdaInstance = new InternalReflectionDynamicAccess();
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+
printUserError(Arrays.toString(classes));
35+
rdaInstance.register(condition, classes);
36+
}
37+
38+
@Override
39+
public void registerClassLookup(RegistrationCondition condition, String className) {
40+
printUserError(className + "for lookup");
41+
rdaInstance.registerClassLookup(condition, className);
42+
}
43+
44+
@Override
45+
public void register(RegistrationCondition condition, Executable... methods) {
46+
printUserError(Arrays.toString(methods));
47+
rdaInstance.register(condition, methods);
48+
}
49+
50+
@Override
51+
public void register(RegistrationCondition condition, Field... fields) {
52+
printUserError(Arrays.toString(fields));
53+
rdaInstance.register(condition, fields);
54+
}
55+
56+
@Override
57+
public void registerForSerialization(RegistrationCondition condition, Class<?>... classes) {
58+
printUserError(Arrays.toString(classes));
59+
rdaInstance.registerForSerialization(condition, classes);
60+
}
61+
62+
@Override
63+
public void registerForJNIAccess(RegistrationCondition condition, Class<?>... classes) {
64+
printUserError(Arrays.toString(classes));
65+
rdaInstance.registerForJNIAccess(condition, classes);
66+
}
67+
68+
private void printUserError(String registrationEntry) {
69+
UserError.guarantee(!afterRegistrationFinished, "Registration for runtime access after Feature#afterRegistration is not allowed. You tried to register: %s", registrationEntry);
70+
}
71+
}

0 commit comments

Comments
 (0)