Skip to content

Commit 4c94b47

Browse files
loicottetelkorchi
authored andcommitted
Backport GR-50432: Allow fields to be registered for reflection without being made reachable
1 parent ad0aec4 commit 4c94b47

File tree

4 files changed

+52
-29
lines changed

4 files changed

+52
-29
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MissingReflectionRegistrationUtils.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ public static void forField(Class<?> declaringClass, String fieldName) {
5454
report(exception);
5555
}
5656

57+
public static MissingReflectionRegistrationError errorForQueriedOnlyField(Field field) {
58+
MissingReflectionRegistrationError exception = new MissingReflectionRegistrationError(errorMessage("read or write field", field.toString()),
59+
field.getClass(), field.getDeclaringClass(), field.getName(), null);
60+
report(exception);
61+
/*
62+
* If report doesn't throw, we throw the exception anyway since this is a Native
63+
* Image-specific error that is unrecoverable in any case.
64+
*/
65+
return exception;
66+
}
67+
5768
public static void forMethod(Class<?> declaringClass, String methodName, Class<?>[] paramTypes) {
5869
StringJoiner paramTypeNames = new StringJoiner(", ", "(", ")");
5970
if (paramTypes != null) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/target/Target_jdk_internal_misc_Unsafe_Reflection.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import com.oracle.svm.core.SubstrateUtil;
3232
import com.oracle.svm.core.annotate.Substitute;
3333
import com.oracle.svm.core.annotate.TargetClass;
34-
import com.oracle.svm.core.util.VMError;
34+
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
3535

3636
@TargetClass(className = "jdk.internal.misc.Unsafe")
3737
@SuppressWarnings({"static-method"})
@@ -80,13 +80,10 @@ static long getFieldOffset(Target_java_lang_reflect_Field field) {
8080
throw new NullPointerException();
8181
}
8282
int offset = field.root == null ? field.offset : field.root.offset;
83-
if (offset > 0) {
84-
return offset;
83+
if (offset <= 0) {
84+
throw MissingReflectionRegistrationUtils.errorForQueriedOnlyField(SubstrateUtil.cast(field, Field.class));
8585
}
86-
throw VMError.unsupportedFeature("The offset of " + field + " is accessed without the field being first registered as unsafe accessed. " +
87-
"Please register the field as unsafe accessed. You can do so with a reflection configuration that " +
88-
"contains an entry for the field with the attribute \"allowUnsafeAccess\": true. Such a configuration " +
89-
"file can be generated for you. Read BuildConfiguration.md and Reflection.md for details.");
86+
return offset;
9087
}
9188

9289
}

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.oracle.svm.core.TypeResult;
3535
import com.oracle.svm.core.configure.ConfigurationTypeDescriptor;
3636
import com.oracle.svm.hosted.ImageClassLoader;
37+
import com.oracle.svm.hosted.reflect.ReflectionDataBuilder;
3738

3839
public class ReflectionRegistryAdapter extends RegistryAdapter {
3940
private final RuntimeReflectionSupport reflectionSupport;
@@ -89,16 +90,12 @@ public void registerSigners(ConfigurationCondition condition, Class<?> type) {
8990

9091
@Override
9192
public void registerPublicFields(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {
92-
if (!queriedOnly) {
93-
reflectionSupport.registerAllFieldsQuery(condition, type);
94-
}
93+
((ReflectionDataBuilder) reflectionSupport).registerAllFieldsQuery(condition, queriedOnly, type);
9594
}
9695

9796
@Override
9897
public void registerDeclaredFields(ConfigurationCondition condition, boolean queriedOnly, Class<?> type) {
99-
if (!queriedOnly) {
100-
reflectionSupport.registerAllDeclaredFieldsQuery(condition, type);
101-
}
98+
((ReflectionDataBuilder) reflectionSupport).registerAllDeclaredFieldsQuery(condition, queriedOnly, type);
10299
}
103100

104101
@Override

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

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -409,50 +409,58 @@ public void registerConstructorLookup(ConfigurationCondition condition, Class<?>
409409
@Override
410410
public void register(ConfigurationCondition condition, boolean finalIsWritable, Field... fields) {
411411
checkNotSealed();
412-
registerInternal(condition, fields);
412+
registerInternal(condition, false, fields);
413413
}
414414

415-
private void registerInternal(ConfigurationCondition condition, Field... fields) {
415+
private void registerInternal(ConfigurationCondition condition, boolean queriedOnly, Field... fields) {
416416
register(analysisUniverse -> registerConditionalConfiguration(condition, () -> {
417417
for (Field field : fields) {
418-
analysisUniverse.getBigbang().postTask(debug -> registerField(field));
418+
analysisUniverse.getBigbang().postTask(debug -> registerField(queriedOnly, field));
419419
}
420420
}));
421421
}
422422

423423
@Override
424424
public void registerAllFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
425+
registerAllFieldsQuery(condition, false, clazz);
426+
}
427+
428+
public void registerAllFieldsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
425429
checkNotSealed();
426430
for (Class<?> current = clazz; current != null; current = current.getSuperclass()) {
427431
final Class<?> currentLambda = current;
428432
registerConditionalConfiguration(condition, () -> setQueryFlag(currentLambda, ALL_FIELDS_FLAG));
429433
}
430434
try {
431-
registerInternal(condition, clazz.getFields());
435+
registerInternal(condition, queriedOnly, clazz.getFields());
432436
} catch (LinkageError e) {
433437
/* Ignore the error */
434438
}
435439
}
436440

437441
@Override
438442
public void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
443+
registerAllDeclaredFieldsQuery(condition, false, clazz);
444+
}
445+
446+
public void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, boolean queriedOnly, Class<?> clazz) {
439447
checkNotSealed();
440448
registerConditionalConfiguration(condition, () -> setQueryFlag(clazz, ALL_DECLARED_FIELDS_FLAG));
441449
try {
442-
registerInternal(condition, clazz.getDeclaredFields());
450+
registerInternal(condition, queriedOnly, clazz.getDeclaredFields());
443451
} catch (LinkageError e) {
444452
/* Ignore the error */
445453
}
446454
}
447455

448-
private void registerField(Field reflectField) {
456+
private void registerField(boolean queriedOnly, Field reflectField) {
449457
if (SubstitutionReflectivityFilter.shouldExclude(reflectField, metaAccess, universe)) {
450458
return;
451459
}
452460

453461
AnalysisField analysisField = metaAccess.lookupJavaField(reflectField);
454462
if (registeredFields.put(analysisField, reflectField) == null) {
455-
registerTypesForField(analysisField, reflectField);
463+
registerTypesForField(analysisField, reflectField, true);
456464
AnalysisType declaringClass = analysisField.getDeclaringClass();
457465

458466
/*
@@ -467,13 +475,21 @@ private void registerField(Field reflectField) {
467475
processAnnotationField(reflectField);
468476
}
469477
}
478+
479+
/*
480+
* We need to run this even if the method has already been registered, in case it was only
481+
* registered as queried.
482+
*/
483+
if (!queriedOnly) {
484+
registerTypesForField(analysisField, reflectField, false);
485+
}
470486
}
471487

472488
@Override
473489
public void registerFieldLookup(ConfigurationCondition condition, Class<?> declaringClass, String fieldName) {
474490
checkNotSealed();
475491
try {
476-
registerInternal(condition, declaringClass.getDeclaredField(fieldName));
492+
registerInternal(condition, false, declaringClass.getDeclaredField(fieldName));
477493
} catch (NoSuchFieldException e) {
478494
registerConditionalConfiguration(condition, () -> negativeFieldLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet()).add(fieldName));
479495
}
@@ -640,13 +656,15 @@ private Object[] getEnclosingMethodInfo(Class<?> clazz) {
640656
}
641657
}
642658

643-
private void registerTypesForField(AnalysisField analysisField, Field reflectField) {
644-
/*
645-
* Reflection accessors use Unsafe, so ensure that all reflectively accessible fields are
646-
* registered as unsafe-accessible, whether they have been explicitly registered or their
647-
* Field object is reachable in the image heap.
648-
*/
649-
analysisField.registerAsUnsafeAccessed("is registered for reflection.");
659+
private void registerTypesForField(AnalysisField analysisField, Field reflectField, boolean queriedOnly) {
660+
if (!queriedOnly) {
661+
/*
662+
* Reflection accessors use Unsafe, so ensure that all reflectively accessible fields
663+
* are registered as unsafe-accessible, whether they have been explicitly registered or
664+
* their Field object is reachable in the image heap.
665+
*/
666+
analysisField.registerAsUnsafeAccessed("is registered for reflection.");
667+
}
650668

651669
/*
652670
* The generic signature is parsed at run time, so we need to make all the types necessary
@@ -998,7 +1016,7 @@ public void registerHeapReflectionField(Field reflectField, ScanReason reason) {
9981016
throw new UnsupportedFeatureException("Registering new field for reflection when the image heap is already sealed: " + reflectField);
9991017
}
10001018
if (!SubstitutionReflectivityFilter.shouldExclude(reflectField, metaAccess, universe)) {
1001-
registerTypesForField(analysisField, reflectField);
1019+
registerTypesForField(analysisField, reflectField, false);
10021020
if (analysisField.getDeclaringClass().isAnnotation()) {
10031021
processAnnotationField(reflectField);
10041022
}

0 commit comments

Comments
 (0)