Skip to content

Commit 865366f

Browse files
committed
Register all fields from reflectively-accessed types as accessed
1 parent 6e0dfd8 commit 865366f

File tree

16 files changed

+76
-47
lines changed

16 files changed

+76
-47
lines changed

sdk/src/org.graalvm.nativeimage/snapshot.sigtest

+1
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,7 @@ meth public !varargs static void register(boolean,boolean,java.lang.reflect.Fiel
11081108
meth public !varargs static void register(boolean,java.lang.reflect.Field[])
11091109
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="21.1")
11101110
meth public !varargs static void register(java.lang.Class<?>[])
1111+
meth public static void register(java.lang.Class<?>,boolean)
11111112
meth public !varargs static void register(java.lang.reflect.Executable[])
11121113
meth public !varargs static void register(java.lang.reflect.Field[])
11131114
meth public !varargs static void registerAsQueried(java.lang.reflect.Executable[])

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -66,7 +66,7 @@ public final class RuntimeJNIAccess {
6666
* @since 22.3
6767
*/
6868
public static void register(Class<?>... classes) {
69-
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(ConfigurationCondition.alwaysTrue(), classes);
69+
ImageSingletons.lookup(RuntimeJNIAccessSupport.class).register(ConfigurationCondition.alwaysTrue(), false, classes);
7070
}
7171

7272
/**

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -69,7 +69,18 @@ public final class RuntimeReflection {
6969
* @since 19.0
7070
*/
7171
public static void register(Class<?>... classes) {
72-
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), classes);
72+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), true, classes);
73+
}
74+
75+
/**
76+
* Makes the provided class available for reflection at run time, allowing to opt out of
77+
* registering all fields of the type for reflective access. A call to {@link Class#forName} for
78+
* the names of the classes will return the classes at run time.
79+
*
80+
* @since 24.2
81+
*/
82+
public static void register(Class<?> clazz, boolean registerAllFields) {
83+
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(ConfigurationCondition.alwaysTrue(), registerAllFields, clazz);
7384
}
7485

7586
/**

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@
4545
import java.util.Arrays;
4646

4747
public interface ReflectionRegistry {
48-
default void register(ConfigurationCondition condition, Class<?>... classes) {
49-
Arrays.stream(classes).forEach(clazz -> register(condition, clazz));
48+
default void register(ConfigurationCondition condition, boolean registerAllFields, Class<?>... classes) {
49+
Arrays.stream(classes).forEach(clazz -> register(condition, registerAllFields, clazz));
5050
}
5151

52-
void register(ConfigurationCondition condition, Class<?> clazz);
52+
void register(ConfigurationCondition condition, boolean registerAllFields, Class<?> clazz);
5353

5454
void register(ConfigurationCondition condition, boolean queriedOnly, Executable... methods);
5555

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public TypeResult<ConfigurationType> resolveType(UnresolvedConfigurationConditio
5555
}
5656

5757
@Override
58-
public void registerType(UnresolvedConfigurationCondition condition, ConfigurationType type) {
58+
public void registerType(UnresolvedConfigurationCondition condition, boolean registerAllFields, ConfigurationType type) {
5959
VMError.guarantee(condition.equals(type.getCondition()), "condition is already a part of the type");
6060
configuration.add(type);
6161
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/LegacyReflectionConfigurationParser.java

+14-8
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
import java.util.List;
3030
import java.util.Optional;
3131

32-
import com.oracle.svm.util.LogUtils;
3332
import org.graalvm.collections.EconomicMap;
3433
import org.graalvm.collections.MapCursor;
3534
import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;
3635

3736
import com.oracle.svm.core.TypeResult;
37+
import com.oracle.svm.util.LogUtils;
3838

3939
final class LegacyReflectionConfigurationParser<C, T> extends ReflectionConfigurationParser<C, T> {
4040

@@ -46,9 +46,9 @@ final class LegacyReflectionConfigurationParser<C, T> extends ReflectionConfigur
4646

4747
private final boolean treatAllNameEntriesAsType;
4848

49-
LegacyReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
49+
LegacyReflectionConfigurationParser(String combinedFileKey, ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
5050
boolean printMissingElements, boolean treatAllNameEntriesAsType) {
51-
super(conditionResolver, delegate, strictConfiguration, printMissingElements);
51+
super(combinedFileKey, conditionResolver, delegate, strictConfiguration, printMissingElements);
5252
this.treatAllNameEntriesAsType = treatAllNameEntriesAsType;
5353
}
5454

@@ -94,7 +94,9 @@ protected void parseClass(EconomicMap<String, Object> data) {
9494

9595
C queryCondition = isType ? conditionResolver.alwaysTrue() : condition;
9696
T clazz = result.get();
97-
delegate.registerType(conditionResult.get(), clazz);
97+
boolean registerAllFields = combinedFileKey.equals(REFLECTION_KEY) && asBoolean(data.get("allDeclaredFields", true), "allDeclaredFields must be a boolean") &&
98+
asBoolean(data.get("allPublicFields", true), "allPublicFields must be a boolean");
99+
delegate.registerType(conditionResult.get(), registerAllFields, clazz);
98100

99101
if (!unsafeAllocatedWarningTriggered && data.containsKey("unsafeAllocated")) {
100102
unsafeAllocatedWarningTriggered = true;
@@ -105,8 +107,10 @@ protected void parseClass(EconomicMap<String, Object> data) {
105107
registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, clazz));
106108
registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, clazz));
107109
registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, clazz));
108-
registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz));
109-
registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
110+
if (!registerAllFields) {
111+
registerIfNotDefault(data, true, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz));
112+
registerIfNotDefault(data, true, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
113+
}
110114
registerIfNotDefault(data, isType, clazz, "allDeclaredClasses", () -> delegate.registerDeclaredClasses(queryCondition, clazz));
111115
registerIfNotDefault(data, isType, clazz, "allRecordComponents", () -> delegate.registerRecordComponents(queryCondition, clazz));
112116
registerIfNotDefault(data, isType, clazz, "allPermittedSubclasses", () -> delegate.registerPermittedSubclasses(queryCondition, clazz));
@@ -117,7 +121,7 @@ protected void parseClass(EconomicMap<String, Object> data) {
117121
registerIfNotDefault(data, isType, clazz, "queryAllPublicConstructors", () -> delegate.registerPublicConstructors(queryCondition, true, clazz));
118122
registerIfNotDefault(data, isType, clazz, "queryAllDeclaredMethods", () -> delegate.registerDeclaredMethods(queryCondition, true, clazz));
119123
registerIfNotDefault(data, isType, clazz, "queryAllPublicMethods", () -> delegate.registerPublicMethods(queryCondition, true, clazz));
120-
if (isType) {
124+
if (isType && !registerAllFields) {
121125
/*
122126
* Fields cannot be registered as queried only by the user, we register them
123127
* unconditionally if the class is registered via "type".
@@ -138,7 +142,9 @@ protected void parseClass(EconomicMap<String, Object> data) {
138142
parseMethods(condition, true, asList(value, "Attribute 'queriedMethods' must be an array of method descriptors"), clazz);
139143
break;
140144
case "fields":
141-
parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz);
145+
if (!registerAllFields) {
146+
parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz);
147+
}
142148
break;
143149
}
144150
} catch (LinkageError e) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParser.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,16 @@ public abstract class ReflectionConfigurationParser<C, T> extends ConfigurationP
4646

4747
protected final ConfigurationConditionResolver<C> conditionResolver;
4848
protected final ReflectionConfigurationParserDelegate<C, T> delegate;
49+
protected final String combinedFileKey;
4950
private final boolean printMissingElements;
5051

51-
public ReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
52+
public ReflectionConfigurationParser(String combinedFileKey, ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
5253
boolean printMissingElements) {
5354
super(strictConfiguration);
5455
this.conditionResolver = conditionResolver;
5556
this.printMissingElements = printMissingElements;
5657
this.delegate = delegate;
58+
this.combinedFileKey = combinedFileKey;
5759
}
5860

5961
public static <C, T> ReflectionConfigurationParser<C, T> create(String combinedFileKey, boolean strictMetadata,
@@ -62,7 +64,7 @@ public static <C, T> ReflectionConfigurationParser<C, T> create(String combinedF
6264
if (strictMetadata) {
6365
return new ReflectionMetadataParser<>(combinedFileKey, conditionResolver, delegate, strictConfiguration, printMissingElements);
6466
} else {
65-
return new LegacyReflectionConfigurationParser<>(conditionResolver, delegate, strictConfiguration, printMissingElements, treatAllEntriesAsType);
67+
return new LegacyReflectionConfigurationParser<>(combinedFileKey, conditionResolver, delegate, strictConfiguration, printMissingElements, treatAllEntriesAsType);
6668
}
6769
}
6870

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionConfigurationParserDelegate.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public interface ReflectionConfigurationParserDelegate<C, T> {
3232

3333
TypeResult<T> resolveType(C condition, ConfigurationTypeDescriptor typeDescriptor, boolean allowPrimitives);
3434

35-
void registerType(C condition, T type);
35+
void registerType(C condition, boolean registerAllFields, T type);
3636

3737
void registerPublicClasses(C condition, T type);
3838

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ReflectionMetadataParser.java

+11-7
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,10 @@ class ReflectionMetadataParser<C, T> extends ReflectionConfigurationParser<C, T>
4141
"allDeclaredConstructors", "allPublicConstructors", "allDeclaredMethods", "allPublicMethods", "allDeclaredFields", "allPublicFields",
4242
"methods", "fields", "unsafeAllocated");
4343

44-
private final String combinedFileKey;
4544

4645
ReflectionMetadataParser(String combinedFileKey, ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
4746
boolean printMissingElements) {
48-
super(conditionResolver, delegate, strictConfiguration, printMissingElements);
49-
this.combinedFileKey = combinedFileKey;
47+
super(combinedFileKey, conditionResolver, delegate, strictConfiguration, printMissingElements);
5048
}
5149

5250
@Override
@@ -87,7 +85,9 @@ protected void parseClass(EconomicMap<String, Object> data) {
8785

8886
C queryCondition = conditionResolver.alwaysTrue();
8987
T clazz = result.get();
90-
delegate.registerType(condition, clazz);
88+
boolean registerAllFields = combinedFileKey.equals(REFLECTION_KEY) && asBoolean(data.get("allDeclaredFields", true), "allDeclaredFields must be a boolean") &&
89+
asBoolean(data.get("allPublicFields", true), "allPublicFields must be a boolean");
90+
delegate.registerType(condition, registerAllFields, clazz);
9191

9292
delegate.registerDeclaredClasses(queryCondition, clazz);
9393
delegate.registerRecordComponents(queryCondition, clazz);
@@ -110,8 +110,10 @@ protected void parseClass(EconomicMap<String, Object> data) {
110110
registerIfNotDefault(data, false, clazz, "allPublicConstructors", () -> delegate.registerPublicConstructors(condition, false, clazz));
111111
registerIfNotDefault(data, false, clazz, "allDeclaredMethods", () -> delegate.registerDeclaredMethods(condition, false, clazz));
112112
registerIfNotDefault(data, false, clazz, "allPublicMethods", () -> delegate.registerPublicMethods(condition, false, clazz));
113-
registerIfNotDefault(data, false, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz));
114-
registerIfNotDefault(data, false, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
113+
if (!registerAllFields) {
114+
registerIfNotDefault(data, true, clazz, "allDeclaredFields", () -> delegate.registerDeclaredFields(condition, false, clazz));
115+
registerIfNotDefault(data, true, clazz, "allPublicFields", () -> delegate.registerPublicFields(condition, false, clazz));
116+
}
115117

116118
MapCursor<String, Object> cursor = data.getEntries();
117119
while (cursor.advance()) {
@@ -123,7 +125,9 @@ protected void parseClass(EconomicMap<String, Object> data) {
123125
parseMethods(condition, false, asList(value, "Attribute 'methods' must be an array of method descriptors"), clazz);
124126
break;
125127
case "fields":
126-
parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz);
128+
if (!registerAllFields) {
129+
parseFields(condition, asList(value, "Attribute 'fields' must be an array of field descriptors"), clazz);
130+
}
127131
break;
128132
}
129133
} catch (LinkageError e) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ public class ReflectionRegistryAdapter extends RegistryAdapter {
4848
}
4949

5050
@Override
51-
public void registerType(ConfigurationCondition condition, Class<?> type) {
52-
super.registerType(condition, type);
51+
public void registerType(ConfigurationCondition condition, boolean registerAllFields, Class<?> type) {
52+
super.registerType(condition, registerAllFields, type);
5353
if (Proxy.isProxyClass(type)) {
5454
proxyRegistry.accept(condition, Arrays.stream(type.getInterfaces()).map(Class::getTypeName).toList());
5555
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ public static RegistryAdapter create(ReflectionRegistry registry, ProxyRegistry
6565
}
6666

6767
@Override
68-
public void registerType(ConfigurationCondition condition, Class<?> type) {
69-
registry.register(condition, type);
68+
public void registerType(ConfigurationCondition condition, boolean registerAllFields, Class<?> type) {
69+
registry.register(condition, registerAllFields, type);
7070
}
7171

7272
@Override
@@ -131,12 +131,12 @@ private TypeResult<Class<?>> resolveProxyType(ProxyConfigurationTypeDescriptor t
131131

132132
@Override
133133
public void registerPublicClasses(ConfigurationCondition condition, Class<?> type) {
134-
registry.register(condition, type.getClasses());
134+
registry.register(condition, false, type.getClasses());
135135
}
136136

137137
@Override
138138
public void registerDeclaredClasses(ConfigurationCondition condition, Class<?> type) {
139-
registry.register(condition, type.getDeclaredClasses());
139+
registry.register(condition, false, type.getDeclaredClasses());
140140
}
141141

142142
@Override

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public void afterRegistration(AfterRegistrationAccess arg) {
209209
ClassInitializationSupport.singleton());
210210
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> parser = ConfigurationParserUtils.create(JNI_KEY, true, conditionResolver, runtimeSupport, null, access.getImageClassLoader());
211211
loadedConfigurations = ConfigurationParserUtils.parseAndRegisterConfigurationsFromCombinedFile(parser, access.getImageClassLoader(), "JNI");
212-
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> legacyParser = ConfigurationParserUtils.create(null, false, conditionResolver, runtimeSupport, null,
212+
ReflectionConfigurationParser<ConfigurationCondition, Class<?>> legacyParser = ConfigurationParserUtils.create(JNI_KEY, false, conditionResolver, runtimeSupport, null,
213213
access.getImageClassLoader());
214214
loadedConfigurations += ConfigurationParserUtils.parseAndRegisterConfigurations(legacyParser, access.getImageClassLoader(), "JNI",
215215
ConfigurationFiles.Options.JNIConfigurationFiles, ConfigurationFiles.Options.JNIConfigurationResources, ConfigurationFile.JNI.getFileName());
@@ -219,7 +219,7 @@ private class JNIRuntimeAccessibilitySupportImpl extends ConditionalConfiguratio
219219
implements RuntimeJNIAccessSupport {
220220

221221
@Override
222-
public void register(ConfigurationCondition condition, Class<?> clazz) {
222+
public void register(ConfigurationCondition condition, boolean registerAllFields, Class<?> clazz) {
223223
Objects.requireNonNull(clazz, () -> nullErrorMessage("class"));
224224
abortIfSealed();
225225
registerConditionalConfiguration(condition, (cnd) -> newClasses.add(clazz));
@@ -251,7 +251,7 @@ private void registerFields(boolean finalIsWritable, Field[] fields) {
251251
@Override
252252
public void registerClassLookup(ConfigurationCondition condition, String typeName) {
253253
try {
254-
register(condition, Class.forName(typeName));
254+
register(condition, false, Class.forName(typeName));
255255
} catch (ClassNotFoundException e) {
256256
newNegativeClassLookups.add(typeName);
257257
}

0 commit comments

Comments
 (0)