Skip to content

Commit ae86944

Browse files
committed
Fix preserved elements support with complete reflection types
1 parent 2a1499d commit ae86944

File tree

3 files changed

+67
-39
lines changed

3 files changed

+67
-39
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public static InternalReflectiveAccess singleton() {
5555
public void register(AccessCondition condition, Class<?>... classes) {
5656
for (Class<?> clazz : classes) {
5757
rrsInstance.register(condition, clazz);
58-
rrsInstance.registerClassMetadata(condition, clazz);
58+
rrsInstance.registerClassMetadata(condition, clazz, false);
5959
}
6060
}
6161

@@ -89,7 +89,7 @@ public void register(AccessCondition condition, Field... fields) {
8989
public void registerForSerialization(AccessCondition condition, Class<?>... classes) {
9090
RuntimeSerializationSupport.singleton().register(condition, classes);
9191
for (Class<?> clazz : classes) {
92-
rrsInstance.registerClassMetadata(condition, clazz);
92+
rrsInstance.registerClassMetadata(condition, clazz, false);
9393
}
9494
}
9595

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

Lines changed: 64 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import static org.graalvm.nativeimage.dynamicaccess.AccessCondition.unconditional;
4141

4242
import java.lang.annotation.AnnotationFormatError;
43+
import java.lang.reflect.AnnotatedElement;
4344
import java.lang.reflect.Constructor;
4445
import java.lang.reflect.Executable;
4546
import java.lang.reflect.Field;
@@ -85,7 +86,6 @@
8586
import com.oracle.graal.pointsto.meta.AnalysisMethod;
8687
import com.oracle.graal.pointsto.meta.AnalysisType;
8788
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
88-
import com.oracle.svm.core.FutureDefaultsOptions;
8989
import com.oracle.svm.core.MissingRegistrationUtils;
9090
import com.oracle.svm.core.configure.ConditionalRuntimeValue;
9191
import com.oracle.svm.core.configure.RuntimeDynamicAccessMetadata;
@@ -150,12 +150,15 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
150150
*/
151151
private final Map<Class<?>, Set<Class<?>>> innerClasses = new ConcurrentHashMap<>();
152152
private final Map<Class<?>, Integer> enabledQueriesFlags = new ConcurrentHashMap<>();
153-
private final Map<AnalysisType, Map<AnalysisField, ConditionalRuntimeValue<Field>>> registeredFields = new ConcurrentHashMap<>();
153+
private final Map<AnalysisType, Map<AnalysisField, RegisteredMemberData<Field>>> registeredFields = new ConcurrentHashMap<>();
154154
private final Set<AnalysisField> hidingFields = ConcurrentHashMap.newKeySet();
155-
private final Map<AnalysisType, Map<AnalysisMethod, ConditionalRuntimeValue<Executable>>> registeredMethods = new ConcurrentHashMap<>();
155+
private final Map<AnalysisType, Map<AnalysisMethod, RegisteredMemberData<Executable>>> registeredMethods = new ConcurrentHashMap<>();
156156
private final Map<AnalysisMethod, Object> methodAccessors = new ConcurrentHashMap<>();
157157
private final Set<AnalysisMethod> hidingMethods = ConcurrentHashMap.newKeySet();
158158

159+
private record RegisteredMemberData<T extends AnnotatedElement>(ConditionalRuntimeValue<T> member, boolean queriedOnly) {
160+
}
161+
159162
// Heap reflection data
160163
private final Set<DynamicHub> heapDynamicHubs = ConcurrentHashMap.newKeySet();
161164
private final Map<AnalysisField, Field> heapFields = new ConcurrentHashMap<>();
@@ -222,9 +225,7 @@ public void register(AccessCondition condition, boolean preserved, Class<?> claz
222225
Objects.requireNonNull(clazz, () -> nullErrorMessage("class", "reflection"));
223226
runConditionalInAnalysisTask(condition, (cnd) -> {
224227
registerClass(cnd, clazz, true, preserved);
225-
if (FutureDefaultsOptions.completeReflectionTypes()) {
226-
registerClassMetadata(cnd, clazz);
227-
}
228+
registerClassMetadata(cnd, clazz, preserved);
228229
});
229230
}
230231

@@ -246,18 +247,18 @@ public void registerAllClassesQuery(AccessCondition condition, boolean preserved
246247
});
247248
}
248249

249-
public void registerClassMetadata(AccessCondition condition, Class<?> clazz) {
250-
registerAllDeclaredFieldsQuery(condition, true, false, clazz);
251-
registerAllFieldsQuery(condition, true, false, clazz);
252-
registerAllDeclaredMethodsQuery(condition, true, false, clazz);
253-
registerAllMethodsQuery(condition, true, false, clazz);
254-
registerAllDeclaredConstructorsQuery(condition, true, false, clazz);
255-
registerAllConstructorsQuery(condition, true, false, clazz);
256-
registerAllDeclaredClassesQuery(condition, false, clazz);
257-
registerAllClassesQuery(condition, false, clazz);
250+
public void registerClassMetadata(AccessCondition condition, Class<?> clazz, boolean preserved) {
251+
registerAllDeclaredFieldsQuery(condition, true, preserved, clazz);
252+
registerAllFieldsQuery(condition, true, preserved, clazz);
253+
registerAllDeclaredMethodsQuery(condition, true, preserved, clazz);
254+
registerAllMethodsQuery(condition, true, preserved, clazz);
255+
registerAllDeclaredConstructorsQuery(condition, true, preserved, clazz);
256+
registerAllConstructorsQuery(condition, true, preserved, clazz);
257+
registerAllDeclaredClassesQuery(condition, preserved, clazz);
258+
registerAllClassesQuery(condition, preserved, clazz);
258259
registerAllRecordComponentsQuery(condition, clazz);
259-
registerAllPermittedSubclassesQuery(condition, false, clazz);
260-
registerAllNestMembersQuery(condition, false, clazz);
260+
registerAllPermittedSubclassesQuery(condition, preserved, clazz);
261+
registerAllNestMembersQuery(condition, preserved, clazz);
261262
registerAllSignersQuery(condition, clazz);
262263
}
263264

@@ -466,24 +467,29 @@ private void registerMethod(AccessCondition cnd, boolean queriedOnly, boolean pr
466467
var classMethods = registeredMethods.computeIfAbsent(declaringType, _ -> new ConcurrentHashMap<>());
467468
var shouldRegisterReachabilityHandler = classMethods.isEmpty();
468469

469-
boolean registered = false;
470-
ConditionalRuntimeValue<Executable> conditionalValue = classMethods.get(analysisMethod);
471-
if (conditionalValue == null) {
472-
var newConditionalValue = new ConditionalRuntimeValue<>(RuntimeDynamicAccessMetadata.emptySet(preserved), reflectExecutable);
473-
conditionalValue = classMethods.putIfAbsent(analysisMethod, newConditionalValue);
474-
if (conditionalValue == null) {
475-
conditionalValue = newConditionalValue;
476-
registered = true;
470+
boolean exists = classMethods.containsKey(analysisMethod);
471+
ConditionalRuntimeValue<Executable> conditionalValue = classMethods.compute(analysisMethod, (_, methodData) -> {
472+
if (methodData == null || (methodData.queriedOnly() && !queriedOnly)) {
473+
/*
474+
* The dynamic access metadata needs to be reset when registering a queried-only
475+
* element as accessed.
476+
*/
477+
return new RegisteredMemberData<>(new ConditionalRuntimeValue<>(RuntimeDynamicAccessMetadata.emptySet(preserved), reflectExecutable), queriedOnly);
478+
} else {
479+
if (!preserved && methodData.queriedOnly() == queriedOnly) {
480+
var value = methodData.member();
481+
value.getDynamicAccessMetadata().setNotPreserved();
482+
}
483+
return methodData;
477484
}
478-
} else if (!preserved) {
479-
conditionalValue.getDynamicAccessMetadata().setNotPreserved();
480-
}
485+
}).member();
486+
481487
if (!queriedOnly) {
482488
/* queryOnly methods are conditioned by the type itself */
483489
conditionalValue.getDynamicAccessMetadata().addCondition(cnd);
484490
}
485491

486-
if (registered) {
492+
if (!exists) {
487493
registerTypesForMethod(analysisMethod, reflectExecutable);
488494
Class<?> declaringClass = declaringType.getJavaClass();
489495

@@ -642,12 +648,24 @@ private void registerField(AccessCondition cnd, boolean queriedOnly, boolean pre
642648
var classFields = registeredFields.computeIfAbsent(declaringClass, _ -> new ConcurrentHashMap<>());
643649
boolean exists = classFields.containsKey(analysisField);
644650
boolean shouldRegisterReachabilityHandler = classFields.isEmpty();
645-
var cndValue = classFields.computeIfAbsent(analysisField, _ -> new ConditionalRuntimeValue<>(RuntimeDynamicAccessMetadata.emptySet(preserved), reflectField));
646-
if (exists) {
647-
if (!preserved) {
648-
cndValue.getDynamicAccessMetadata().setNotPreserved();
651+
652+
ConditionalRuntimeValue<Field> cndValue = classFields.compute(analysisField, (_, fieldData) -> {
653+
if (fieldData == null || (fieldData.queriedOnly() && !queriedOnly)) {
654+
/*
655+
* The dynamic access metadata needs to be reset when registering an element as
656+
* accessed
657+
*/
658+
return new RegisteredMemberData<>(new ConditionalRuntimeValue<>(RuntimeDynamicAccessMetadata.emptySet(preserved), reflectField), queriedOnly);
659+
} else {
660+
if (!preserved && fieldData.queriedOnly() == queriedOnly) {
661+
var value = fieldData.member();
662+
value.getDynamicAccessMetadata().setNotPreserved();
663+
}
664+
return fieldData;
649665
}
650-
} else {
666+
}).member();
667+
668+
if (!exists) {
651669
registerTypesForField(analysisField, reflectField, queriedOnly);
652670

653671
/*
@@ -1269,13 +1287,23 @@ public int getEnabledReflectionQueries(Class<?> clazz) {
12691287
@Override
12701288
public Map<AnalysisType, Map<AnalysisField, ConditionalRuntimeValue<Field>>> getReflectionFields() {
12711289
assert isSealed();
1272-
return Collections.unmodifiableMap(registeredFields);
1290+
return filterRegisteredMemberData(registeredFields);
12731291
}
12741292

12751293
@Override
12761294
public Map<AnalysisType, Map<AnalysisMethod, ConditionalRuntimeValue<Executable>>> getReflectionExecutables() {
12771295
assert isSealed();
1278-
return Collections.unmodifiableMap(registeredMethods);
1296+
return filterRegisteredMemberData(registeredMethods);
1297+
}
1298+
1299+
private static <A, R extends AnnotatedElement> Map<AnalysisType, Map<A, ConditionalRuntimeValue<R>>> filterRegisteredMemberData(Map<AnalysisType, Map<A, RegisteredMemberData<R>>> map) {
1300+
/*
1301+
* Return only the ConditionalRuntimeValue, the queriedOnly boolean is not relevant once the
1302+
* builder is sealed.
1303+
*/
1304+
return map.entrySet().stream().collect(
1305+
Collectors.toUnmodifiableMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream().collect(
1306+
Collectors.toUnmodifiableMap(Map.Entry::getKey, e2 -> e2.getValue().member()))));
12791307
}
12801308

12811309
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
318318
* Querying Object members is allowed to enable these accesses on array classes, since those
319319
* don't define any additional members.
320320
*/
321-
reflectionData.registerClassMetadata(AccessCondition.unconditional(), Object.class);
321+
reflectionData.registerClassMetadata(AccessCondition.unconditional(), Object.class, false);
322322
}
323323

324324
@Override

0 commit comments

Comments
 (0)