Skip to content

Commit 53372f7

Browse files
author
Mihailo Markovic
committed
Introduction of new API for metadata registration based on conditions. User must create DynamicAccess object via factory method inside AfterRegistrationAccess.
1 parent 2b3fb97 commit 53372f7

14 files changed

+347
-133
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package org.graalvm.nativeimage.hosted;
2+
3+
import java.lang.reflect.Executable;
4+
import java.lang.reflect.Field;
5+
import java.util.Arrays;
6+
import java.util.Locale;
7+
import java.util.Objects;
8+
9+
import org.graalvm.nativeimage.ImageSingletons;
10+
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
11+
12+
/**
13+
* Dynamic access allows users to register classes, methods, fields that should be available for
14+
* reflection, serialization and JNI access at runtime, as well as registration of Java resources
15+
* and ResourceBundles that should be available at runtime.
16+
*
17+
* It should only be used at {@link Feature#afterRegistration}.
18+
*/
19+
public interface DynamicAccess {
20+
/**
21+
* Makes the provided classes available for reflection at run time and all of their accessible
22+
* members available for reflection queries at run time. A call to {@link Class#forName} for the
23+
* names of the classes will return the classes at run time.
24+
*
25+
* @param condition needs to be satisfied for inclusion of types for reflection at runtime
26+
*/
27+
void registerForReflection(InclusionCondition condition, Class<?>... classes);
28+
29+
/**
30+
* Makes the provided class available for reflection at run time if the condition is satisfied.
31+
* A call to {@link Class#forName} for the name of the class will return the class (if it
32+
* exists) or a {@link ClassNotFoundException} at run time.
33+
*/
34+
void registerClassLookup(InclusionCondition condition, String className);
35+
36+
/**
37+
* Makes the provided methods available for reflection at run time if the condition is
38+
* satisfied. The methods will be returned by {@link Class#getMethod},
39+
* {@link Class#getDeclaredMethod(String, Class[])}, and all the other methods on {@link Class}
40+
* that return a single method.
41+
*/
42+
void registerForReflection(InclusionCondition condition, Executable... methods);
43+
44+
/**
45+
* Makes the provided classes available for serialization and reflection at runtime if the
46+
* condition is satisfied.
47+
*/
48+
void registerForSerialization(InclusionCondition condition, Class<?>... classes);
49+
50+
/**
51+
* Makes the provided classes available for JNI access at run time if the condition is
52+
* satisfied. Needed when native code looks up Java classes via <a href=
53+
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#findclass">FindClass</a>.
54+
*/
55+
void registerForJNIAccess(InclusionCondition condition, Class<?>... classes);
56+
57+
/**
58+
* Makes the provided methods available for JNI access at run time if the condition is
59+
* satisfied. Needed when native code looks up Java methods via <a href=
60+
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid">GetMethodID</a>
61+
* or <a href=
62+
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticmethodid">GetStaticMethodID</a>.
63+
*/
64+
void registerForJNIAccess(InclusionCondition condition, Executable... methods);
65+
66+
/**
67+
* Makes the provided methods available for JNI access at run time if the condition is
68+
* satisfied. Needed when native code looks up Java methods via <a href=
69+
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid">GetMethodID</a>
70+
* or <a href=
71+
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticmethodid">GetStaticMethodID</a>.
72+
*/
73+
void registerForJNIAccess(InclusionCondition condition, Field... fields);
74+
75+
/// TO-DO: Ask what is best for resource registration
76+
77+
/**
78+
* Make Java resource {@code resourcePath} from {@code module} available at run time. If the
79+
* given {@code module} is unnamed, the resource is looked up on the classpath instead.
80+
*/
81+
public static void addResource(Module module, String resourcePath) {
82+
Objects.requireNonNull(module);
83+
Objects.requireNonNull(resourcePath);
84+
ImageSingletons.lookup(RuntimeResourceSupport.class).addResource(module, resourcePath, "Manually added via RuntimeResourceAccess");
85+
}
86+
87+
/**
88+
* Inject a Java resource at {@code resourcePath} in {@code module} with the specified
89+
* {@code resourceContent}. At runtime the resource can be accessed as if it was part of the
90+
* original application. If the given {@code module} is unnamed, the resource is placed on the
91+
* classpath instead.
92+
*/
93+
public static void addResource(Module module, String resourcePath, byte[] resourceContent) {
94+
Objects.requireNonNull(module);
95+
Objects.requireNonNull(resourcePath);
96+
Objects.requireNonNull(resourceContent);
97+
ImageSingletons.lookup(RuntimeResourceSupport.class).injectResource(module, resourcePath, resourceContent, "Manually added via RuntimeResourceAccess");
98+
ImageSingletons.lookup(RuntimeResourceSupport.class).addCondition(InclusionCondition.alwaysTrue(), module, resourcePath);
99+
}
100+
101+
/**
102+
* Make Java ResourceBundle that is specified by a {@code baseBundleName} and {@code locales}
103+
* from module {@code module} available at run time. If the given {@code module} is unnamed, the
104+
* ResourceBundle is looked up on the classpath instead.
105+
*/
106+
public static void addResourceBundle(Module module, String baseBundleName, Locale[] locales) {
107+
Objects.requireNonNull(locales);
108+
RuntimeResourceSupport.singleton().addResourceBundles(InclusionCondition.alwaysTrue(),
109+
withModuleName(module, baseBundleName), Arrays.asList(locales));
110+
}
111+
112+
/**
113+
* Make Java ResourceBundle that is specified by a {@code bundleName} from module {@code module}
114+
* available at run time. If the given {@code module} is unnamed, the ResourceBundle is looked
115+
* up on the classpath instead.
116+
*/
117+
public static void addResourceBundle(Module module, String bundleName) {
118+
RuntimeResourceSupport.singleton().addResourceBundles(InclusionCondition.alwaysTrue(),
119+
withModuleName(module, bundleName));
120+
}
121+
122+
private static String withModuleName(Module module, String str) {
123+
Objects.requireNonNull(module);
124+
Objects.requireNonNull(str);
125+
return (module.isNamed() ? module.getName() : "ALL-UNNAMED") + ":" + str;
126+
}
127+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ 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+
DynamicAccess createDynamicAccess();
174180
}
175181

176182
/**

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,22 @@
4242

4343
import org.graalvm.nativeimage.impl.TypeCondition;
4444

45+
/**
46+
* Condition that needs to be satisfied for inclusion of elements for runtime access.
47+
* {@link InclusionCondition} is used with {@link DynamicAccess} for programmatic registration of
48+
* metadata.
49+
* <p>
50+
* Currently, there is only two types of condition:
51+
* <li><code>typeReached</code> (the default) that signifies that the type must be both reachable by
52+
* static analysis at build time, and reached at run time. A type is reached at run time, right
53+
* before the class-initialization routine starts for that type, or any of the type's subtypes are
54+
* reached.</li>
55+
* <p>
56+
*
57+
* User can only create <code>typeReached</code> conditions, since <code>typeReachable</code> are
58+
* deprecated. Conditions are created via {@link InclusionCondition#alwaysTrue} and
59+
* {@link InclusionCondition#typeReached} factory methods.
60+
*/
4561
public interface InclusionCondition {
4662

4763
/**
@@ -55,8 +71,7 @@ static InclusionCondition alwaysTrue() {
5571
}
5672

5773
/**
58-
* Creates the default type-reached condition that is satisfied when the type is reached at
59-
* runtime.
74+
* Creates the type-reached condition that is satisfied when the type is reached at runtime.
6075
*
6176
* @param type that has to be reached for this condition to be satisfied
6277
* @return instance of the condition

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public static void registerAllDeclaredConstructors(Class<?> declaringClass) {
220220
* @since 23.0
221221
*/
222222
public static void registerAllFields(Class<?> declaringClass) {
223-
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllFields(MetadataCondition.alwaysTrue(), declaringClass);
223+
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllFields(InclusionCondition.alwaysTrue(), declaringClass);
224224
}
225225

226226
/**

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,24 @@
4444
import java.lang.reflect.Field;
4545
import java.util.Arrays;
4646

47+
import org.graalvm.nativeimage.hosted.InclusionCondition;
48+
4749
public interface ReflectionRegistry {
48-
default void register(MetadataCondition condition, Class<?>... classes) {
50+
default void register(InclusionCondition condition, Class<?>... classes) {
4951
Arrays.stream(classes).forEach(clazz -> register(condition, false, clazz));
5052
}
5153

52-
void register(MetadataCondition condition, boolean unsafeAllocated, Class<?> clazz);
54+
void register(InclusionCondition condition, boolean unsafeAllocated, Class<?> clazz);
5355

54-
void register(MetadataCondition condition, boolean queriedOnly, Executable... methods);
56+
void register(InclusionCondition condition, boolean queriedOnly, Executable... methods);
5557

56-
void register(MetadataCondition condition, boolean finalIsWritable, Field... fields);
58+
void register(InclusionCondition condition, boolean finalIsWritable, Field... fields);
5759

58-
void registerClassLookup(MetadataCondition condition, String typeName);
60+
void registerClassLookup(InclusionCondition condition, String typeName);
5961

60-
void registerFieldLookup(MetadataCondition condition, Class<?> declaringClass, String fieldName);
62+
void registerFieldLookup(InclusionCondition condition, Class<?> declaringClass, String fieldName);
6163

62-
void registerMethodLookup(MetadataCondition condition, Class<?> declaringClass, String methodName, Class<?>... parameterTypes);
64+
void registerMethodLookup(InclusionCondition condition, Class<?> declaringClass, String methodName, Class<?>... parameterTypes);
6365

64-
void registerConstructorLookup(MetadataCondition condition, Class<?> declaringClass, Class<?>... parameterTypes);
66+
void registerConstructorLookup(InclusionCondition condition, Class<?> declaringClass, Class<?>... parameterTypes);
6567
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,21 @@
4545
import java.util.stream.Collectors;
4646

4747
import org.graalvm.nativeimage.ImageSingletons;
48+
import org.graalvm.nativeimage.hosted.InclusionCondition;
4849

4950
public interface RuntimeSerializationSupport<C> {
5051

5152
@SuppressWarnings("unchecked")
52-
static RuntimeSerializationSupport<MetadataCondition> singleton() {
53+
static RuntimeSerializationSupport<InclusionCondition> singleton() {
5354
return ImageSingletons.lookup(RuntimeSerializationSupport.class);
5455
}
5556

5657
void registerIncludingAssociatedClasses(C condition, Class<?> clazz);
5758

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

6065
void register(C condition, String clazz);

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

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,8 @@
4545
import org.graalvm.nativeimage.hosted.InclusionCondition;
4646

4747
/**
48-
* A condition that describes if a reflectively-accessed element in Native Image is visible by the
49-
* user at run time.
50-
* <p>
51-
* Currently, there is only two types of condition:
52-
* <li><code>typeReached</code> (the default) that signifies that the type must be both reachable by
53-
* static analysis at build time, and reached at run time. A type is reached at run time, right
54-
* before the class-initialization routine starts for that type, or any of the type's subtypes are
55-
* reached.</li>
56-
* <li><code>typeReachable</code> (legacy) that signifies that the type must be reachable by static
57-
* analysis at build time.</li>
58-
* <p>
59-
* When {@link TypeCondition#runtimeChecked} is <code>true</code> denotes that this is a
48+
* Type that represents both type-reached and type-reachable condition. When
49+
* {@link TypeCondition#runtimeChecked} is <code>true</code> denotes that this is a
6050
* <code>typeReached</code> condition.
6151
*/
6252
public class TypeCondition implements InclusionCondition {

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

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import java.util.function.Consumer;
3333

3434
import org.graalvm.nativeimage.hosted.Feature;
35-
import org.graalvm.nativeimage.impl.MetadataCondition;
35+
import org.graalvm.nativeimage.hosted.InclusionCondition;
3636
import org.graalvm.nativeimage.impl.TypeCondition;
3737

3838
import com.oracle.svm.core.util.VMError;
@@ -42,7 +42,7 @@ public abstract class ConditionalConfigurationRegistry {
4242
private Feature.BeforeAnalysisAccess beforeAnalysisAccess;
4343
private final Map<Class<?>, Collection<Runnable>> pendingReachabilityHandlers = new ConcurrentHashMap<>();
4444

45-
protected void registerConditionalConfiguration(MetadataCondition condition, Consumer<MetadataCondition> consumer) {
45+
protected void registerConditionalConfiguration(InclusionCondition condition, Consumer<InclusionCondition> consumer) {
4646
Objects.requireNonNull(condition, "Cannot use null value as condition for conditional configuration. Please ensure that you register a non-null condition.");
4747
Objects.requireNonNull(consumer, "Cannot use null value as runnable for conditional configuration. Please ensure that you register a non-null runnable.");
4848
if (((TypeCondition) condition).isRuntimeChecked() && !((TypeCondition) condition).isAlwaysTrue()) {
@@ -53,25 +53,19 @@ protected void registerConditionalConfiguration(MetadataCondition condition, Con
5353
*/
5454
ClassInitializationSupport.singleton().addForTypeReachedTracking(((TypeCondition) condition).getKey());
5555
}
56-
if (MetadataCondition.alwaysTrue().equals(condition)) {
56+
if (((TypeCondition) condition).isAlwaysTrue()) {
5757
/* analysis optimization to include new types as early as possible */
58-
consumer.accept(MetadataCondition.alwaysTrue());
58+
consumer.accept(InclusionCondition.alwaysTrue());
5959
} else {
60-
MetadataCondition runtimeCondition;
61-
if (((TypeCondition) condition).isRuntimeChecked()) {
62-
runtimeCondition = condition;
63-
} else {
64-
runtimeCondition = MetadataCondition.alwaysTrue();
65-
}
60+
InclusionCondition runtimeCondition = ((TypeCondition) condition).isRuntimeChecked() ? condition : InclusionCondition.alwaysTrue();
61+
6662
if (beforeAnalysisAccess == null) {
6763
Collection<Runnable> handlers = pendingReachabilityHandlers.computeIfAbsent(((TypeCondition) condition).getKey(), key -> new ConcurrentLinkedQueue<>());
6864
handlers.add(() -> consumer.accept(runtimeCondition));
6965
} else {
7066
beforeAnalysisAccess.registerReachabilityHandler(access -> consumer.accept(runtimeCondition), ((TypeCondition) condition).getKey());
7167
}
72-
7368
}
74-
7569
}
7670

7771
public void setAnalysisAccess(Feature.BeforeAnalysisAccess beforeAnalysisAccess) {

0 commit comments

Comments
 (0)