Skip to content

Commit a161b2e

Browse files
author
Mihailo Markovic
committed
Implemented new logic for registration of members as JNI accessible
1 parent 148e12e commit a161b2e

File tree

6 files changed

+71
-47
lines changed

6 files changed

+71
-47
lines changed

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

-18
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,4 @@ public interface ReflectionDynamicAccess {
4949
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#findclass">FindClass</a>.
5050
*/
5151
void registerForJNIAccess(InclusionCondition condition, Class<?>... classes);
52-
53-
/**
54-
* Makes the provided methods available for JNI access at runtime if {@code condition} is
55-
* satisfied. Needed when native code looks up Java methods via <a href=
56-
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid">GetMethodID</a>
57-
* or <a href=
58-
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticmethodid">GetStaticMethodID</a>.
59-
*/
60-
void registerForJNIAccess(InclusionCondition condition, Executable... methods);
61-
62-
/**
63-
* Makes the provided methods available for JNI access at runtime if {@code condition} is
64-
* satisfied. Needed when native code looks up Java methods via <a href=
65-
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getmethodid">GetMethodID</a>
66-
* or <a href=
67-
* "https://docs.oracle.com/en/java/javase/17/docs/specs/jni/functions.html#getstaticmethodid">GetStaticMethodID</a>.
68-
*/
69-
void registerForJNIAccess(InclusionCondition condition, Field... fields);
7052
}

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,11 @@
4040
*/
4141
package org.graalvm.nativeimage.impl;
4242

43-
import org.graalvm.nativeimage.hosted.InclusionCondition;
44-
4543
import java.lang.reflect.Constructor;
4644
import java.lang.reflect.Method;
4745
import java.lang.reflect.Modifier;
4846

47+
import org.graalvm.nativeimage.hosted.InclusionCondition;
4948
import org.graalvm.nativeimage.hosted.RuntimeJNIAccess;
5049
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
5150

@@ -57,8 +56,12 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry {
5756

5857
void registerAllFields(InclusionCondition condition, Class<?> clazz);
5958

59+
void registerAllFieldsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz);
60+
6061
void registerAllDeclaredFields(InclusionCondition condition, Class<?> clazz);
6162

63+
void registerAllDeclaredFieldsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz);
64+
6265
void registerAllConstructorsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz);
6366

6467
void registerAllDeclaredConstructorsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz);

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

+3-11
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ public void register(InclusionCondition condition, Class<?>... classes) {
2929
rrsInstance.registerAllDeclaredMethodsQuery(condition, true, clazz);
3030
rrsInstance.registerAllMethodsQuery(condition, true, clazz);
3131
rrsInstance.registerAllDeclaredConstructorsQuery(condition, true, clazz);
32-
rrsInstance.registerAllFields(condition, clazz);
32+
rrsInstance.registerAllConstructorsQuery(condition, true, clazz);
33+
rrsInstance.registerAllFieldsQuery(condition, true, clazz);
34+
rrsInstance.registerAllDeclaredFieldsQuery(condition, true, clazz);
3335
rrsInstance.registerAllNestMembersQuery(condition, clazz);
3436
rrsInstance.registerAllPermittedSubclassesQuery(condition, clazz);
3537
rrsInstance.registerAllRecordComponentsQuery(condition, clazz);
@@ -62,14 +64,4 @@ public void registerForSerialization(InclusionCondition condition, Class<?>... c
6264
public void registerForJNIAccess(InclusionCondition condition, Class<?>... classes) {
6365
rrjInstance.register(condition, classes);
6466
}
65-
66-
@Override
67-
public void registerForJNIAccess(InclusionCondition condition, Executable... methods) {
68-
rrjInstance.register(condition, false, methods);
69-
}
70-
71-
@Override
72-
public void registerForJNIAccess(InclusionCondition condition, Field... fields) {
73-
rrjInstance.register(condition, false, fields);
74-
}
7567
}

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

+1-15
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,6 @@ public void registerForSerialization(InclusionCondition condition, Class<?>... c
6868
public void registerForJNIAccess(InclusionCondition condition, Class<?>... classes) {
6969
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
7070
Arrays.toString(classes));
71-
rdaInstance.register(condition, classes);
72-
}
73-
74-
@Override
75-
public void registerForJNIAccess(InclusionCondition condition, Executable... methods) {
76-
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
77-
Arrays.toString(methods));
78-
rdaInstance.registerForJNIAccess(condition, methods);
79-
}
80-
81-
@Override
82-
public void registerForJNIAccess(InclusionCondition condition, Field... fields) {
83-
UserError.guarantee(!afterRegistrationFinished, "There shouldn't be a registration for runtime access after afterRegistration period. You tried to register: %s",
84-
Arrays.toString(fields));
85-
rdaInstance.registerForJNIAccess(condition, fields);
71+
rdaInstance.registerForJNIAccess(condition, classes);
8672
}
8773
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jni/JNIAccessFeature.java

+30-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.graalvm.nativeimage.hosted.Feature;
5353
import org.graalvm.nativeimage.hosted.InclusionCondition;
5454
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
55+
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
5556
import org.graalvm.word.PointerBase;
5657

5758
import com.oracle.graal.pointsto.BigBang;
@@ -103,6 +104,7 @@
103104
import com.oracle.svm.hosted.meta.KnownOffsetsFeature;
104105
import com.oracle.svm.hosted.meta.MaterializedConstantFields;
105106
import com.oracle.svm.hosted.reflect.NativeImageConditionResolver;
107+
import com.oracle.svm.hosted.reflect.ReflectionDataBuilder;
106108
import com.oracle.svm.hosted.reflect.proxy.DynamicProxyFeature;
107109
import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter;
108110
import com.oracle.svm.util.ReflectionUtil;
@@ -226,7 +228,22 @@ public void register(InclusionCondition condition, boolean unsafeAllocated, Clas
226228
assert !unsafeAllocated : "unsafeAllocated can be only set via Unsafe.allocateInstance, not via JNI.";
227229
Objects.requireNonNull(clazz, () -> nullErrorMessage("class"));
228230
abortIfSealed();
229-
registerConditionalConfiguration(condition, (cnd) -> newClasses.add(clazz));
231+
registerConditionalConfiguration(condition, (cnd) -> {
232+
newClasses.add(clazz);
233+
/*
234+
* All methods and fields that are registered for reflection, must be registered for
235+
* JNI access if declaring type is JNI accessible
236+
*/
237+
Executable[] executables = ((ReflectionDataBuilder) ImageSingletons.lookup(RuntimeReflectionSupport.class)).getNotQueriedOnlyExecutables(clazz);
238+
if (executables.length > 0) {
239+
register(cnd, false, executables);
240+
}
241+
242+
Field[] fields = ((ReflectionDataBuilder) ImageSingletons.lookup(RuntimeReflectionSupport.class)).getReflectiveAccessibleFields(clazz);
243+
if (fields.length > 0) {
244+
register(cnd, false, fields);
245+
}
246+
});
230247
}
231248

232249
@Override
@@ -525,6 +542,18 @@ private static void addNegativeFieldLookup(Class<?> declaringClass, String field
525542
jniClass.addFieldIfAbsent(fieldName, d -> JNIAccessibleField.negativeFieldQuery(jniClass));
526543
}
527544

545+
public Set<Class<?>> getJNIAccessibleTypes() {
546+
return newClasses;
547+
}
548+
549+
public Set<Executable> getJNIAccessibleMethods() {
550+
return newMethods;
551+
}
552+
553+
public Set<Field> getJNIAccessibleFields() {
554+
return newFields.keySet();
555+
}
556+
528557
@Override
529558
@SuppressWarnings("unused")
530559
public void afterAnalysis(AfterAnalysisAccess access) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java

+32
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
import org.graalvm.nativeimage.hosted.InclusionCondition;
7171
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
7272
import org.graalvm.nativeimage.hosted.RuntimeReflection;
73+
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
7374
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
7475

7576
import com.oracle.graal.pointsto.ObjectScanner.ScanReason;
@@ -94,6 +95,7 @@
9495
import com.oracle.svm.hosted.annotation.AnnotationValue;
9596
import com.oracle.svm.hosted.annotation.SubstrateAnnotationExtractor;
9697
import com.oracle.svm.hosted.annotation.TypeAnnotationValue;
98+
import com.oracle.svm.hosted.jni.JNIAccessFeature;
9799
import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter;
98100
import com.oracle.svm.util.LogUtils;
99101
import com.oracle.svm.util.ReflectionUtil;
@@ -121,6 +123,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
121123
private final Map<Class<?>, Set<Class<?>>> innerClasses = new ConcurrentHashMap<>();
122124
private final Map<Class<?>, Integer> enabledQueriesFlags = new ConcurrentHashMap<>();
123125
private final Map<AnalysisType, Map<AnalysisField, ConditionalRuntimeValue<Field>>> registeredFields = new ConcurrentHashMap<>();
126+
private final Set<Field> reflectiveAccessibleFields = new HashSet<>();
124127
private final Set<AnalysisField> hidingFields = ConcurrentHashMap.newKeySet();
125128
private final Map<AnalysisType, Map<AnalysisMethod, ConditionalRuntimeValue<Executable>>> registeredMethods = new ConcurrentHashMap<>();
126129
private final Map<AnalysisMethod, Object> methodAccessors = new ConcurrentHashMap<>();
@@ -471,6 +474,16 @@ private void registerMethod(InclusionCondition cnd, boolean queriedOnly, Executa
471474
return accessor;
472475
});
473476
}
477+
/*
478+
* We must register method that isn't registered as queriedOnly, as JNI accessible if it
479+
* isn't already.
480+
*/
481+
Set<Executable> JNIAccessibleMethods = JNIAccessFeature.singleton().getJNIAccessibleMethods();
482+
Set<Class<?>> JNIAccessibleClasses = JNIAccessFeature.singleton().getJNIAccessibleTypes();
483+
484+
if (!JNIAccessibleMethods.contains(reflectExecutable) && JNIAccessibleClasses.contains(declaringType.getJavaClass()) && !queriedOnly) {
485+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(cnd, false, reflectExecutable);
486+
}
474487
}
475488

476489
@Override
@@ -512,6 +525,7 @@ public void registerAllFields(InclusionCondition condition, Class<?> clazz) {
512525
registerAllFieldsQuery(condition, false, clazz);
513526
}
514527

528+
@Override
515529
public void registerAllFieldsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz) {
516530
runConditionalInAnalysisTask(condition, (cnd) -> {
517531
for (Class<?> current = clazz; current != null; current = current.getSuperclass()) {
@@ -530,6 +544,7 @@ public void registerAllDeclaredFields(InclusionCondition condition, Class<?> cla
530544
registerAllDeclaredFieldsQuery(condition, false, clazz);
531545
}
532546

547+
@Override
533548
public void registerAllDeclaredFieldsQuery(InclusionCondition condition, boolean queriedOnly, Class<?> clazz) {
534549
runConditionalInAnalysisTask(condition, (cnd) -> {
535550
setQueryFlag(clazz, ALL_DECLARED_FIELDS_FLAG);
@@ -595,6 +610,14 @@ private void registerField(InclusionCondition cnd, boolean queriedOnly, Field re
595610
/* queryOnly methods are conditioned on the type itself */
596611
cndValue.getConditions().addCondition(cnd);
597612
registerTypesForField(analysisField, reflectField, false);
613+
reflectiveAccessibleFields.add(reflectField);
614+
}
615+
616+
Set<Field> JNIAccessibleFields = JNIAccessFeature.singleton().getJNIAccessibleFields();
617+
Set<Class<?>> JNIAccessibleClasses = JNIAccessFeature.singleton().getJNIAccessibleTypes();
618+
619+
if (!JNIAccessibleFields.contains(reflectField) && JNIAccessibleClasses.contains(declaringClass.getJavaClass()) && !queriedOnly) {
620+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(cnd, false, reflectField);
598621
}
599622
}
600623

@@ -1151,6 +1174,15 @@ public Object getAccessor(AnalysisMethod method) {
11511174
return methodAccessors.get(method);
11521175
}
11531176

1177+
public Executable[] getNotQueriedOnlyExecutables(Class<?> declaringType) {
1178+
return methodAccessors.keySet().stream().filter(analysisMethod -> analysisMethod.getDeclaringClass().getJavaClass().equals(declaringType)).map(AnalysisMethod::getJavaMethod)
1179+
.collect(Collectors.toSet()).toArray(new Executable[0]);
1180+
}
1181+
1182+
public Field[] getReflectiveAccessibleFields(Class<?> declaringType) {
1183+
return reflectiveAccessibleFields.stream().filter(field -> field.getDeclaringClass().equals(declaringType)).collect(Collectors.toSet()).toArray(new Field[0]);
1184+
}
1185+
11541186
@Override
11551187
public Set<ResolvedJavaField> getHidingReflectionFields() {
11561188
assert sealed;

0 commit comments

Comments
 (0)