Skip to content

Commit 1956782

Browse files
committed
[GR-60238] Include JNI reachability metadata with reflection
PullRequest: graal/20030
2 parents 44f1745 + b66c3e8 commit 1956782

File tree

61 files changed

+1175
-506
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1175
-506
lines changed

docs/reference-manual/native-image/ReachabilityMetadata.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ The _reachability-metadata.json_ configuration contains a single object with one
131131
{
132132
"reflection":[],
133133
"resources":[],
134-
"bundles":[],
135-
"jni":[]
134+
"bundles":[]
136135
}
137136
```
138137
@@ -376,12 +375,14 @@ jclass clazz = FindClass(env, "jni/accessed/Type");
376375
```
377376
looks up the `jni.accessed.Type` class, which can then be used to instantiate `jni.accessed.Type`, invoke its methods or access its fields.
378377

379-
The metadata entry for the above call can *only* be provided via _reachability-metadata.json_. Specify the `type` entry in the `jni` field:
378+
The metadata entry for the above call can *only* be provided via _reachability-metadata.json_. Specify
379+
the `jniAccessible` field in the `type` entry in the `reflection` section:
380380
```json
381381
{
382-
"jni":[
382+
"reflection": [
383383
{
384-
"type": "jni.accessed.Type"
384+
"type": "jni.accessed.Type",
385+
"jniAccessibleType": true
385386
}
386387
]
387388
}
@@ -393,13 +394,15 @@ To access field values, we need to provide field names:
393394
```json
394395
{
395396
"type": "jni.accessed.Type",
397+
"jniAccessible": true,
396398
"fields": [{"name": "value"}]
397399
}
398400
```
399401
To access all fields one can use the following attributes:
400402
```json
401403
{
402404
"type": "jni.accessed.Type",
405+
"jniAccessible": true,
403406
"allDeclaredFields": true,
404407
"allPublicFields": true
405408
}
@@ -410,6 +413,7 @@ To call Java methods from JNI, we must provide metadata for the method signature
410413
```json
411414
{
412415
"type": "jni.accessed.Type",
416+
"jniAccessible": true,
413417
"methods": [
414418
{"name": "<methodName1>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]},
415419
{"name": "<methodName2>", "parameterTypes": ["<param-type1>", "<param-typeI>", "<param-typeN>"]}
@@ -420,6 +424,7 @@ As a convenience, one can allow method invocation for groups of methods by addin
420424
```json
421425
{
422426
"type": "jni.accessed.Type",
427+
"jniAccessible": true,
423428
"allDeclaredConstructors": true,
424429
"allPublicConstructors": true,
425430
"allDeclaredMethods": true,
@@ -429,7 +434,8 @@ As a convenience, one can allow method invocation for groups of methods by addin
429434
`allDeclaredConstructors` and `allDeclaredMethods` allow calls invocations of methods declared on a given type.
430435
`allPublicConstructors` and `allPublicMethods` allow invocations of all public methods defined on a type and all of its supertypes.
431436

432-
To allocate objects of a type with `AllocObject`, the metadata must be stored in the `reflection` section:
437+
To allocate objects of a type with `AllocObject`, the `unsafeAllocated` field must be set, but the `jniAccessible` field
438+
is not required:
433439
```json
434440
{
435441
"reflection": [

docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@
207207
"title": "Allow objects of this class to be serialized and deserialized",
208208
"type": "boolean",
209209
"default": false
210+
},
211+
"jniAccessible": {
212+
"title": "Register the type, including all registered fields and methods, for runtime JNI access",
213+
"type": "boolean",
214+
"default": false
210215
}
211216
},
212217
"additionalProperties": false

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

Lines changed: 0 additions & 121 deletions
This file was deleted.

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1818
* (GR-63494) Recurring callback support is no longer enabled by default. If this feature is needed, please specify `-H:+SupportRecurringCallback` at image build-time.
1919
* (GR-60209) New syntax for configuration of the [Foreign Function & Memory API](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/ForeignInterface.md)
2020
* (GR-64584) Experimental option `-H:+RelativeCodePointers` to significantly reduce relocation entries in position-independent executables and shared libraries.
21+
* (GR-60238) JNI registration is now included as part of the `"reflection"` section of _reachability-metadata.json_ using the `"jniAccessible"` attribute. Registrations performed through the `"jni"` section of _reachability-metadata.json_ and through _jni-config.json_ will still be accepted and parsed correctly.
2122

2223
## GraalVM for JDK 24 (Internal Version 24.2.0)
2324
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

substratevm/mx.substratevm/suite.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,11 @@
11811181
"sdk:NATIVEIMAGE",
11821182
"com.oracle.svm.configure",
11831183
],
1184+
"requiresConcealed": {
1185+
"jdk.internal.vm.ci": [
1186+
"jdk.vm.ci.meta"
1187+
],
1188+
},
11841189
"checkstyle": "com.oracle.svm.test",
11851190
"workingSets": "SVM",
11861191
"annotationProcessors": [
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.configure.test.config;
26+
27+
import java.util.List;
28+
29+
import org.junit.Assert;
30+
import org.junit.Test;
31+
32+
import com.oracle.svm.configure.ClassNameSupport;
33+
import com.oracle.svm.configure.test.AddExports;
34+
35+
import jdk.vm.ci.meta.MetaUtil;
36+
37+
@AddExports({"jdk.internal.vm.ci/jdk.vm.ci.meta"})
38+
public class ClassNameSupportTest {
39+
@Test
40+
public void testNameAliases() {
41+
for (Class<?> clazz : List.of(Object.class, int.class, EnclosingClass.class, EnclosingClass.InnerClass.class, EnclosingClass.StaticInnerClass.class,
42+
new EnclosingClass().anonymousClass().getClass(), Object[].class, int[].class, EnclosingClass.InnerClass[].class)) {
43+
testNameAliasesForClass(clazz);
44+
}
45+
}
46+
47+
private static void testNameAliasesForClass(Class<?> clazz) {
48+
String typeName = clazz.getTypeName();
49+
String reflectionName = clazz.getName();
50+
51+
Assert.assertTrue(ClassNameSupport.isValidTypeName(typeName));
52+
Assert.assertTrue(ClassNameSupport.isValidReflectionName(reflectionName));
53+
54+
Assert.assertEquals(typeName, ClassNameSupport.reflectionNameToTypeName(reflectionName));
55+
Assert.assertEquals(reflectionName, ClassNameSupport.typeNameToReflectionName(typeName));
56+
57+
/* Primitive classes cannot be accessed through JNI */
58+
if (!clazz.isPrimitive()) {
59+
String internalName = MetaUtil.toInternalName(reflectionName);
60+
String jniName = internalName.startsWith("L") ? internalName.substring(1, internalName.length() - 1) : internalName;
61+
62+
Assert.assertTrue(ClassNameSupport.isValidJNIName(jniName));
63+
64+
Assert.assertEquals(typeName, ClassNameSupport.jniNameToTypeName(jniName));
65+
Assert.assertEquals(reflectionName, ClassNameSupport.jniNameToReflectionName(jniName));
66+
Assert.assertEquals(jniName, ClassNameSupport.typeNameToJNIName(typeName));
67+
Assert.assertEquals(jniName, ClassNameSupport.reflectionNameToJNIName(reflectionName));
68+
}
69+
}
70+
}
71+
72+
class EnclosingClass {
73+
Object anonymousClass() {
74+
return new Object() {
75+
@Override
76+
public String toString() {
77+
return null;
78+
}
79+
};
80+
}
81+
82+
class InnerClass {
83+
}
84+
85+
static class StaticInnerClass {
86+
}
87+
}

substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@
3535
import java.util.function.Function;
3636
import java.util.function.Predicate;
3737

38-
import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;
3938
import org.junit.Assert;
4039
import org.junit.Test;
4140

4241
import com.oracle.svm.configure.ConfigurationTypeDescriptor;
4342
import com.oracle.svm.configure.NamedConfigurationTypeDescriptor;
43+
import com.oracle.svm.configure.UnresolvedConfigurationCondition;
4444
import com.oracle.svm.configure.config.ConfigurationFileCollection;
4545
import com.oracle.svm.configure.config.ConfigurationMemberInfo;
4646
import com.oracle.svm.configure.config.ConfigurationMemberInfo.ConfigurationMemberAccessibility;
@@ -97,7 +97,6 @@ public void testSameConfig() {
9797
ConfigurationSet config = loadTraceProcessorFromResourceDirectory(PREVIOUS_CONFIG_DIR_NAME, omittedConfig);
9898
config = config.copyAndSubtract(omittedConfig);
9999

100-
assertTrue(config.getJniConfiguration().isEmpty());
101100
assertTrue(config.getReflectionConfiguration().isEmpty());
102101
assertTrue(config.getProxyConfiguration().isEmpty());
103102
assertTrue(config.getResourceConfiguration().isEmpty());
@@ -112,7 +111,7 @@ public void testConfigDifference() {
112111
config = config.copyAndSubtract(omittedConfig);
113112

114113
doTestGeneratedTypeConfig();
115-
doTestTypeConfig(config.getJniConfiguration());
114+
doTestTypeConfig(config.getReflectionConfiguration());
116115

117116
doTestProxyConfig(config.getProxyConfiguration());
118117

@@ -242,8 +241,8 @@ class TypeMethodsWithFlagsTest {
242241
final Map<ConfigurationMethod, ConfigurationMemberDeclaration> methodsThatMustExist = new HashMap<>();
243242
final Map<ConfigurationMethod, ConfigurationMemberDeclaration> methodsThatMustNotExist = new HashMap<>();
244243

245-
final TypeConfiguration previousConfig = new TypeConfiguration("");
246-
final TypeConfiguration currentConfig = new TypeConfiguration("");
244+
final TypeConfiguration previousConfig = new TypeConfiguration();
245+
final TypeConfiguration currentConfig = new TypeConfiguration();
247246

248247
TypeMethodsWithFlagsTest(ConfigurationMemberDeclaration methodKind) {
249248
this.methodKind = methodKind;

substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@
3535
import java.util.Locale;
3636

3737
import org.graalvm.nativeimage.impl.ConfigurationCondition;
38-
import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;
3938
import org.junit.Assert;
4039
import org.junit.Test;
4140

4241
import com.oracle.svm.configure.ConfigurationParserOption;
4342
import com.oracle.svm.configure.ResourceConfigurationParser;
4443
import com.oracle.svm.configure.ResourcesRegistry;
44+
import com.oracle.svm.configure.UnresolvedConfigurationCondition;
4545
import com.oracle.svm.configure.config.ResourceConfiguration;
4646
import com.oracle.svm.configure.config.conditional.ConfigurationConditionResolver;
4747

0 commit comments

Comments
 (0)