Skip to content

Commit 3cf466e

Browse files
committed
Predicated reflection and JNI config for NI
Introducing predicates for reflection and JNI configuration. The predicate can be only a type name. For example, "predicate": { "whenTypeReachable": "org.graalvm.ReachableClass" }, will apply configuration only if "type org.graalvm.ReachableClass" is reachable. The default predicate for every reflection entry is "java.lang.Object" and as such it will not be printed. The Native Image agent does not emit predicates, but will fuse predicates accordingly. Instead all config of one type, the fusion will happen by the predicate and the type.
1 parent bc7e837 commit 3cf466e

File tree

17 files changed

+395
-157
lines changed

17 files changed

+395
-157
lines changed

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,15 @@ Here, `reflectconfig` is a JSON file in the following format (use `--expert-opti
107107
{ "name" : "format", "parameterTypes" : ["java.lang.String", "java.lang.Object[]"] }
108108
]
109109
},
110-
{
111-
"name" : "java.lang.String$CaseInsensitiveComparator",
112-
"methods" : [
113-
{ "name" : "compare" }
114-
]
115-
}
110+
{
111+
"name" : "java.lang.String$CaseInsensitiveComparator",
112+
"methods" : [
113+
{ "name" : "compare" }
114+
]
115+
}
116116
]
117117
```
118+
118119
The native image builder generates reflection metadata for all classes, methods, and fields referenced in that file.
119120
The `allPublicConstructors`, `allDeclaredConstructors`, `allPublicMethods`, `allDeclaredMethods`, `allPublicFields`, `allDeclaredFields`, `allPublicClasses`, and `allDeclaredClasses` attributes can be used to automatically include an entire set of members of a class.
120121

@@ -125,6 +126,27 @@ Code may also write non-static final fields like `String.value` in this example,
125126
More than one configuration can be used by specifying multiple paths for `ReflectionConfigurationFiles` and separating them with `,`.
126127
Also, `-H:ReflectionConfigurationResources` can be specified to load one or several configuration files from the native image build's class path, such as from a JAR file.
127128

129+
### Predicated Configuration
130+
131+
Each configuration entry is taken into account when a `predicate` is satisfied. The currently supported predicate is `whenTypeReachable` which enables the configuration entry when a provided type is reachable. For example, the reflectively access `sun.misc.Unsafe.theUnsafe` only when `io.netty.util.internal.PlatformDependent0` is used we write
132+
```json
133+
{
134+
"predicate" : { "whenTypeReachable" : "io.netty.util.internal.PlatformDependent0" },
135+
"name" : "sun.misc.Unsafe",
136+
"fields" : [
137+
{ "name" : "theUnsafe" }
138+
]
139+
}
140+
```
141+
142+
If a `predicate` is omitted, Native Image will assume that the predicate is `java.lang.Object`. When the same predicate is used for two distinct elements in two configuration entries, both elements will be included when the predicate is satisfied.
143+
144+
*Predicated configuration* is the *preferred* way to specify reflection configuration: Every reflective access must be done through some piece of code. If that code is not reachable it makes little sense to include the corresponding reflection entry. The consistant usage of `predicate` results in *smaller binaries* and *better build times* as the image builder can selectively include reflectivelly accessed code.
145+
146+
When used with [assisted configuration](BuildConfiguration.md#assisted-configuration-of-native-image-builds), predicated entries of existing configuration will not be fused with collected entries. The collected entries will all have the default predicate `java.lang.Object` that will not be printed.
147+
148+
### Confugration with Features
149+
128150
Alternatively, a custom `Feature` implementation can register program elements before and during the analysis phase of the native image build using the `RuntimeReflection` class. For example:
129151
```java
130152
class RuntimeReflectionRegistrationFeature implements Feature {

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.graalvm.nativeimage.ImageSingletons;
4949
import org.graalvm.nativeimage.Platform;
5050
import org.graalvm.nativeimage.Platforms;
51+
import org.graalvm.nativeimage.impl.ConfigurationPredicate;
5152
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
5253

5354
//Checkstyle: allow reflection
@@ -68,7 +69,7 @@ public final class RuntimeReflection {
6869
* @since 19.0
6970
*/
7071
public static void register(Class<?>... classes) {
71-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(classes);
72+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, classes);
7273
}
7374

7475
/**
@@ -79,7 +80,7 @@ public static void register(Class<?>... classes) {
7980
* @since 19.0
8081
*/
8182
public static void register(Executable... methods) {
82-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(methods);
83+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, methods);
8384
}
8485

8586
/**
@@ -90,7 +91,7 @@ public static void register(Executable... methods) {
9091
* @since 19.0
9192
*/
9293
public static void register(Field... fields) {
93-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(false, fields);
94+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, false, fields);
9495
}
9596

9697
/**
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.nativeimage.impl;
42+
43+
import java.util.Objects;
44+
45+
public final class ConfigurationPredicate {
46+
private final String typeReachability;
47+
public static final ConfigurationPredicate DEFAULT_CONFIGURATION_PREDICATE = new ConfigurationPredicate(Object.class.getTypeName());
48+
49+
public static ConfigurationPredicate create(String typeReachability) {
50+
if (DEFAULT_CONFIGURATION_PREDICATE.typeReachability.equals(typeReachability)) {
51+
return DEFAULT_CONFIGURATION_PREDICATE;
52+
}
53+
return new ConfigurationPredicate(typeReachability);
54+
}
55+
56+
private ConfigurationPredicate(String typeReachability) {
57+
this.typeReachability = typeReachability;
58+
}
59+
60+
public String getTypeReachability() {
61+
return typeReachability;
62+
}
63+
64+
@Override
65+
public boolean equals(Object o) {
66+
if (this == o) {
67+
return true;
68+
}
69+
if (o == null || getClass() != o.getClass()) {
70+
return false;
71+
}
72+
ConfigurationPredicate predicate = (ConfigurationPredicate) o;
73+
return Objects.equals(typeReachability, predicate.typeReachability);
74+
}
75+
76+
@Override
77+
public int hashCode() {
78+
return Objects.hash(typeReachability);
79+
}
80+
81+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@
4444
import java.lang.reflect.Field;
4545

4646
public interface ReflectionRegistry {
47-
void register(Class<?>... classes);
47+
void register(ConfigurationPredicate predicate, Class<?>... classes);
4848

49-
void register(Executable... methods);
49+
void register(ConfigurationPredicate predicate, Executable... methods);
5050

51-
void register(boolean finalIsWritable, Field... fields);
51+
void register(ConfigurationPredicate predicate, boolean finalIsWritable, Field... fields);
5252

5353
}

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

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

38-
import com.oracle.svm.configure.config.PredefinedClassesConfiguration;
38+
import org.graalvm.nativeimage.impl.ConfigurationPredicate;
3939
import org.junit.Assert;
4040
import org.junit.Test;
4141

@@ -44,6 +44,7 @@
4444
import com.oracle.svm.configure.config.ConfigurationSet;
4545
import com.oracle.svm.configure.config.ConfigurationType;
4646
import com.oracle.svm.configure.config.FieldInfo;
47+
import com.oracle.svm.configure.config.PredefinedClassesConfiguration;
4748
import com.oracle.svm.configure.config.ProxyConfiguration;
4849
import com.oracle.svm.configure.config.ResourceConfiguration;
4950
import com.oracle.svm.configure.config.SerializationConfiguration;
@@ -145,8 +146,8 @@ private static void doTestTypeConfig(TypeConfiguration typeConfig) {
145146
}
146147

147148
private static void doTestExpectedMissingTypes(TypeConfiguration typeConfig) {
148-
Assert.assertNull(typeConfig.get("FlagTestA"));
149-
Assert.assertNull(typeConfig.get("FlagTestB"));
149+
Assert.assertNull(typeConfig.get(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, "FlagTestA"));
150+
Assert.assertNull(typeConfig.get(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, "FlagTestB"));
150151
}
151152

152153
private static void doTestTypeFlags(TypeConfiguration typeConfig) {
@@ -199,7 +200,7 @@ private static void doTestSerializationConfig(SerializationConfiguration seriali
199200
}
200201

201202
private static ConfigurationType getConfigTypeOrFail(TypeConfiguration typeConfig, String typeName) {
202-
ConfigurationType type = typeConfig.get(typeName);
203+
ConfigurationType type = typeConfig.get(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, typeName);
203204
Assert.assertNotNull(type);
204205
return type;
205206
}
@@ -261,7 +262,7 @@ Map<ConfigurationMethod, ConfigurationMemberKind> getMethodsMap(ConfigurationMem
261262
void populateConfig() {
262263
ConfigurationType oldType = new ConfigurationType(getTypeName());
263264
setFlags(oldType);
264-
previousConfig.add(oldType);
265+
previousConfig.add(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, oldType);
265266

266267
ConfigurationType newType = new ConfigurationType(getTypeName());
267268
for (Map.Entry<ConfigurationMethod, ConfigurationMemberKind> methodEntry : methodsThatMustExist.entrySet()) {
@@ -270,7 +271,7 @@ void populateConfig() {
270271
for (Map.Entry<ConfigurationMethod, ConfigurationMemberKind> methodEntry : methodsThatMustNotExist.entrySet()) {
271272
newType.addMethod(methodEntry.getKey().getName(), methodEntry.getKey().getInternalSignature(), methodEntry.getValue());
272273
}
273-
currentConfig.add(newType);
274+
currentConfig.add(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, newType);
274275
}
275276

276277
void setFlags(ConfigurationType config) {
@@ -294,7 +295,7 @@ String getTypeName() {
294295

295296
void doTest() {
296297
String name = getTypeName();
297-
ConfigurationType configurationType = currentConfig.get(name);
298+
ConfigurationType configurationType = currentConfig.get(ConfigurationPredicate.DEFAULT_CONFIGURATION_PREDICATE, name);
298299
if (methodsThatMustExist.size() == 0) {
299300
Assert.assertNull("Generated configuration type " + name + " exists. Expected it to be cleared as it is empty.", configurationType);
300301
} else {

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,6 @@ public void setAllPublicConstructors() {
316316

317317
@Override
318318
public void printJson(JsonWriter writer) throws IOException {
319-
writer.append('{').indent().newline();
320319
writer.quote("name").append(':').quote(qualifiedJavaName);
321320
optionallyPrintJsonBoolean(writer, haveAllDeclaredFields(), "allDeclaredFields");
322321
optionallyPrintJsonBoolean(writer, haveAllPublicFields(), "allPublicFields");
@@ -337,8 +336,6 @@ public void printJson(JsonWriter writer) throws IOException {
337336
Comparator.comparing(ConfigurationMethod::getName).thenComparing(Comparator.nullsFirst(Comparator.comparing(ConfigurationMethod::getInternalSignature))),
338337
JsonPrintable::printJson);
339338
}
340-
writer.unindent().newline();
341-
writer.append('}');
342339
}
343340

344341
private static void printField(Map.Entry<String, FieldInfo> entry, JsonWriter w) throws IOException {

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ParserConfigurationAdapter.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.List;
2828

2929
import com.oracle.svm.core.TypeResult;
30+
import org.graalvm.nativeimage.impl.ConfigurationPredicate;
3031
import com.oracle.svm.core.configure.ReflectionConfigurationParserDelegate;
3132

3233
public class ParserConfigurationAdapter implements ReflectionConfigurationParserDelegate<ConfigurationType> {
@@ -38,81 +39,81 @@ public ParserConfigurationAdapter(TypeConfiguration configuration) {
3839
}
3940

4041
@Override
41-
public TypeResult<ConfigurationType> resolveTypeResult(String typeName) {
42-
ConfigurationType type = configuration.get(typeName);
42+
public TypeResult<ConfigurationType> resolveTypeResult(ConfigurationPredicate predicate, String typeName) {
43+
ConfigurationType type = configuration.get(predicate, typeName);
4344
ConfigurationType result = type != null ? type : new ConfigurationType(typeName);
4445
return TypeResult.forType(typeName, result);
4546
}
4647

4748
@Override
48-
public void registerType(ConfigurationType type) {
49-
configuration.add(type);
49+
public void registerType(ConfigurationPredicate predicate, ConfigurationType type) {
50+
configuration.add(predicate, type);
5051
}
5152

5253
@Override
53-
public void registerField(ConfigurationType type, String fieldName, boolean finalButWritable) {
54+
public void registerField(ConfigurationPredicate predicate, ConfigurationType type, String fieldName, boolean finalButWritable) {
5455
type.addField(fieldName, ConfigurationMemberKind.PRESENT, finalButWritable);
5556
}
5657

5758
@Override
58-
public boolean registerAllMethodsWithName(ConfigurationType type, String methodName) {
59+
public boolean registerAllMethodsWithName(ConfigurationPredicate predicate, ConfigurationType type, String methodName) {
5960
type.addMethodsWithName(methodName, ConfigurationMemberKind.PRESENT);
6061
return true;
6162
}
6263

6364
@Override
64-
public boolean registerAllConstructors(ConfigurationType type) {
65+
public boolean registerAllConstructors(ConfigurationPredicate predicate, ConfigurationType type) {
6566
type.addMethodsWithName(ConfigurationMethod.CONSTRUCTOR_NAME, ConfigurationMemberKind.PRESENT);
6667
return true;
6768
}
6869

6970
@Override
70-
public void registerMethod(ConfigurationType type, String methodName, List<ConfigurationType> methodParameterTypes) {
71+
public void registerMethod(ConfigurationPredicate predicate, ConfigurationType type, String methodName, List<ConfigurationType> methodParameterTypes) {
7172
type.addMethod(methodName, ConfigurationMethod.toInternalParamsSignature(methodParameterTypes), ConfigurationMemberKind.PRESENT);
7273
}
7374

7475
@Override
75-
public void registerConstructor(ConfigurationType type, List<ConfigurationType> methodParameterTypes) {
76+
public void registerConstructor(ConfigurationPredicate predicate, ConfigurationType type, List<ConfigurationType> methodParameterTypes) {
7677
type.addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, ConfigurationMethod.toInternalParamsSignature(methodParameterTypes), ConfigurationMemberKind.PRESENT);
7778
}
7879

7980
@Override
80-
public void registerPublicClasses(ConfigurationType type) {
81+
public void registerPublicClasses(ConfigurationPredicate predicate, ConfigurationType type) {
8182
type.setAllPublicClasses();
8283
}
8384

8485
@Override
85-
public void registerDeclaredClasses(ConfigurationType type) {
86+
public void registerDeclaredClasses(ConfigurationPredicate predicate, ConfigurationType type) {
8687
type.setAllDeclaredClasses();
8788
}
8889

8990
@Override
90-
public void registerPublicFields(ConfigurationType type) {
91+
public void registerPublicFields(ConfigurationPredicate predicate, ConfigurationType type) {
9192
type.setAllPublicFields();
9293
}
9394

9495
@Override
95-
public void registerDeclaredFields(ConfigurationType type) {
96+
public void registerDeclaredFields(ConfigurationPredicate predicate, ConfigurationType type) {
9697
type.setAllDeclaredFields();
9798
}
9899

99100
@Override
100-
public void registerPublicMethods(ConfigurationType type) {
101+
public void registerPublicMethods(ConfigurationPredicate predicate, ConfigurationType type) {
101102
type.setAllPublicMethods();
102103
}
103104

104105
@Override
105-
public void registerDeclaredMethods(ConfigurationType type) {
106+
public void registerDeclaredMethods(ConfigurationPredicate predicate, ConfigurationType type) {
106107
type.setAllDeclaredMethods();
107108
}
108109

109110
@Override
110-
public void registerPublicConstructors(ConfigurationType type) {
111+
public void registerPublicConstructors(ConfigurationPredicate predicate, ConfigurationType type) {
111112
type.setAllPublicConstructors();
112113
}
113114

114115
@Override
115-
public void registerDeclaredConstructors(ConfigurationType type) {
116+
public void registerDeclaredConstructors(ConfigurationPredicate predicate, ConfigurationType type) {
116117
type.setAllDeclaredConstructors();
117118
}
118119

0 commit comments

Comments
 (0)