Skip to content

Commit 2d50264

Browse files
committed
Fixes for various linkage errors
These happen when all elements are included for reflection.
1 parent aae7098 commit 2d50264

File tree

7 files changed

+64
-25
lines changed

7 files changed

+64
-25
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ClassForNameSupport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;
2828

29+
import java.lang.reflect.Modifier;
2930
import java.util.EnumSet;
3031
import java.util.Objects;
3132

@@ -154,7 +155,7 @@ public void registerNegativeQuery(ConfigurationCondition condition, String class
154155

155156
@Platforms(Platform.HOSTED_ONLY.class)
156157
public void registerUnsafeAllocated(ConfigurationCondition condition, Class<?> clazz) {
157-
if (!clazz.isArray()) {
158+
if (!clazz.isArray() && !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
158159
var conditionSet = unsafeInstantiatedClasses.putIfAbsent(clazz, RuntimeConditionSet.createHosted(condition));
159160
if (conditionSet != null) {
160161
conditionSet.addCondition(condition);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.lang.reflect.Method;
3232
import java.lang.reflect.Modifier;
3333
import java.lang.reflect.Proxy;
34+
import java.lang.reflect.RecordComponent;
3435
import java.util.Comparator;
3536
import java.util.EnumSet;
3637
import java.util.HashMap;
@@ -646,7 +647,7 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) {
646647
throw new UnsupportedFeatureException(message);
647648
}
648649
if (originalClass.isRecord()) {
649-
for (var recordComponent : originalClass.getRecordComponents()) {
650+
for (var recordComponent : ReflectionUtil.linkageSafeQuery(originalClass, new RecordComponent[0], Class::getRecordComponents)) {
650651
if (WordBase.class.isAssignableFrom(recordComponent.getType())) {
651652
throw UserError.abort("Records cannot use Word types. " +
652653
"The equals/hashCode/toString implementation of records uses method handles, and Word types are not supported as parameters of method handle invocations. " +

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,19 @@
3131
import java.util.Objects;
3232

3333
import com.oracle.svm.core.util.VMError;
34+
import com.oracle.svm.util.ReflectionUtil;
3435

3536
public final class RecordUtils {
3637

3738
public static Method[] getRecordComponentAccessorMethods(Class<?> clazz) {
38-
return Arrays.stream(clazz.getRecordComponents())
39+
return Arrays.stream(ReflectionUtil.linkageSafeQuery(clazz, new RecordComponent[0], Class::getRecordComponents))
3940
.map(RecordComponent::getAccessor)
4041
.filter(Objects::nonNull)
4142
.toArray(Method[]::new);
4243
}
4344

4445
public static Constructor<?> getCanonicalRecordConstructor(Class<?> clazz) {
45-
Class<?>[] paramTypes = Arrays.stream(clazz.getRecordComponents())
46+
Class<?>[] paramTypes = Arrays.stream(ReflectionUtil.linkageSafeQuery(clazz, new RecordComponent[0], Class::getRecordComponents))
4647
.map(RecordComponent::getType)
4748
.toArray(Class<?>[]::new);
4849
try {

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

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@
6464
import java.util.Set;
6565
import java.util.concurrent.Callable;
6666
import java.util.concurrent.ConcurrentHashMap;
67+
import java.util.concurrent.atomic.AtomicBoolean;
6768
import java.util.function.Consumer;
6869
import java.util.stream.Collectors;
6970

71+
import com.oracle.svm.core.jdk.UninterruptibleUtils;
7072
import org.graalvm.nativeimage.ImageSingletons;
7173
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
7274
import org.graalvm.nativeimage.hosted.RuntimeReflection;
@@ -501,6 +503,8 @@ public void registerMethodLookup(ConfigurationCondition condition, Class<?> decl
501503
} catch (NoSuchMethodException e) {
502504
negativeMethodLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
503505
.add(new AnalysisMethod.Signature(methodName, metaAccess.lookupJavaTypes(parameterTypes)));
506+
} catch (LinkageError le) {
507+
registerLinkageError(declaringClass, le, methodLookupExceptions);
504508
}
505509
});
506510
}
@@ -514,6 +518,8 @@ public void registerConstructorLookup(ConfigurationCondition condition, Class<?>
514518
} catch (NoSuchMethodException e) {
515519
negativeConstructorLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
516520
.add(metaAccess.lookupJavaTypes(parameterTypes));
521+
} catch (LinkageError le) {
522+
registerLinkageError(declaringClass, le, constructorLookupExceptions);
517523
}
518524
});
519525
}
@@ -525,7 +531,7 @@ public void register(ConfigurationCondition condition, boolean finalIsWritable,
525531
}
526532

527533
@Override
528-
public void registerAllFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
534+
public void registerAllFields(ConfigurationCondition condition, Class<?> clazz) {
529535
registerAllFieldsQuery(condition, false, clazz);
530536
}
531537

@@ -544,7 +550,7 @@ public void registerAllFieldsQuery(ConfigurationCondition condition, boolean que
544550
}
545551

546552
@Override
547-
public void registerAllDeclaredFieldsQuery(ConfigurationCondition condition, Class<?> clazz) {
553+
public void registerAllDeclaredFields(ConfigurationCondition condition, Class<?> clazz) {
548554
registerAllDeclaredFieldsQuery(condition, false, clazz);
549555
}
550556

@@ -575,16 +581,13 @@ private void registerField(ConfigurationCondition cnd, boolean queriedOnly, Fiel
575581

576582
var classFields = registeredFields.computeIfAbsent(declaringClass, t -> new ConcurrentHashMap<>());
577583
boolean exists = classFields.containsKey(analysisField);
584+
// TODO open a ticket to disallow
578585
boolean shouldRegisterReachabilityHandler = classFields.isEmpty();
579586
var cndValue = classFields.computeIfAbsent(analysisField, f -> new ConditionalRuntimeValue<>(RuntimeConditionSet.emptySet(), reflectField));
580-
if (!queriedOnly) {
581-
/* queryOnly methods are conditioned by the type itself */
582-
cndValue.getConditions().addCondition(cnd);
583-
}
584-
585587
if (!exists) {
586-
registerTypesForField(analysisField, reflectField, true);
588+
registerTypesForField(analysisField, reflectField, queriedOnly);
587589

590+
// TODO bulk it up
588591
/*
589592
* The image needs to know about subtypes shadowing fields registered for reflection to
590593
* ensure the correctness of run-time reflection queries.
@@ -611,10 +614,12 @@ private void registerField(ConfigurationCondition cnd, boolean queriedOnly, Fiel
611614
}
612615

613616
/*
614-
* We need to run this even if the method has already been registered, in case it was only
617+
* We need to run this even if the field has already been registered, in case it was only
615618
* registered as queried.
616619
*/
617620
if (!queriedOnly) {
621+
/* queryOnly methods are conditioned on the type itself */
622+
cndValue.getConditions().addCondition(cnd);
618623
registerTypesForField(analysisField, reflectField, false);
619624
}
620625
}
@@ -631,6 +636,8 @@ public void registerFieldLookup(ConfigurationCondition condition, Class<?> decla
631636
* not necessary.
632637
*/
633638
negativeFieldLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet()).add(fieldName);
639+
} catch (LinkageError le) {
640+
registerLinkageError(declaringClass, le, fieldLookupExceptions);
634641
}
635642
});
636643
}
@@ -752,7 +759,7 @@ private void registerTypesForClass(AnalysisType analysisType, Class<?> clazz) {
752759
}
753760

754761
private void registerRecordComponents(Class<?> clazz) {
755-
RecordComponent[] recordComponents = clazz.getRecordComponents();
762+
RecordComponent[] recordComponents = ReflectionUtil.linkageSafeQuery(clazz, null, Class::getRecordComponents);
756763
if (recordComponents == null) {
757764
return;
758765
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/serialize/SerializationFeature.java

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
import java.util.Set;
5151
import java.util.concurrent.ConcurrentHashMap;
5252

53+
import com.oracle.svm.core.MissingRegistrationSupport;
54+
import com.oracle.svm.core.MissingRegistrationUtils;
55+
import com.oracle.svm.core.SubstrateOptions;
56+
import com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils;
5357
import org.graalvm.nativeimage.ImageSingletons;
5458
import org.graalvm.nativeimage.hosted.Feature;
5559
import org.graalvm.nativeimage.hosted.RuntimeReflection;
@@ -504,7 +508,8 @@ private void registerForSerialization(ConfigurationCondition cnd, Class<?> seria
504508
* serialization class consistency, so need to register all constructors, methods and
505509
* fields.
506510
*/
507-
registerSerializationUIDElements(serializationTargetClass, true); // if MRE
511+
boolean legacyFullyRegister = !MissingRegistrationSupport.singleton().reportMissingRegistrationErrors(serializationTargetClass);
512+
registerSerializationUIDElements(serializationTargetClass, legacyFullyRegister);
508513

509514
/*
510515
* Required by jdk.internal.reflect.ReflectionFactory.newConstructorForSerialization
@@ -527,11 +532,17 @@ private void registerForSerialization(ConfigurationCondition cnd, Class<?> seria
527532

528533
Class<?> iter = serializationTargetClass;
529534
while (iter != null) {
530-
Arrays.stream(iter.getDeclaredFields()).map(Field::getType).forEach(type -> {
531-
RuntimeReflection.registerAllDeclaredMethods(type);
532-
RuntimeReflection.registerAllDeclaredFields(type);
533-
RuntimeReflection.registerAllDeclaredConstructors(type);
534-
});
535+
536+
Arrays.stream(ReflectionUtil.linkageSafeQuery(iter, new Field[0], Class::getDeclaredFields))
537+
.map(Field::getType).forEach(type -> {
538+
if (legacyFullyRegister) {
539+
RuntimeReflection.registerAllDeclaredMethods(type);
540+
RuntimeReflection.registerAllDeclaredFields(type);
541+
RuntimeReflection.registerAllDeclaredConstructors(type);
542+
} else {
543+
RuntimeReflection.register(type);
544+
}
545+
});
535546
iter = iter.getSuperclass();
536547
}
537548
}
@@ -550,11 +561,12 @@ static void registerSerializationUIDElements(Class<?> serializationTargetClass,
550561
RuntimeReflection.registerAllDeclaredFields(serializationTargetClass);
551562
if (fullyRegister) {
552563
/* This is here a legacy that we can't remove as it is a breaking change */
553-
RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
554-
RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
555-
RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
564+
RuntimeReflection.register(ReflectionUtil.linkageSafeQuery(serializationTargetClass, new Constructor[0], Class::getDeclaredConstructors));
565+
RuntimeReflection.register(ReflectionUtil.linkageSafeQuery(serializationTargetClass, new Method[0], Class::getDeclaredMethods));
566+
RuntimeReflection.register(ReflectionUtil.linkageSafeQuery(serializationTargetClass, new Field[0], Class::getDeclaredFields));
567+
568+
RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
556569
}
557-
RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
558570
}
559571

560572
public void afterAnalysis() {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.oracle.svm.core.annotate.InjectAccessors;
3737
import com.oracle.svm.core.annotate.TargetClass;
3838

39+
import jdk.graal.compiler.api.replacements.Fold;
3940
import jdk.vm.ci.meta.ResolvedJavaType;
4041

4142
/**
@@ -74,6 +75,8 @@ public static boolean shouldExclude(Executable method, AnalysisMetaAccess metaAc
7475
return true;
7576
} else if (aMethod.isAnnotationPresent(Delete.class)) {
7677
return true; // accesses would fail at runtime
78+
} else if (aMethod.isAnnotationPresent(Fold.class)) {
79+
return true; // accesses can contain hosted elements
7780
} else if (aMethod.isSynthetic() && aMethod.getDeclaringClass().isAnnotationPresent(TargetClass.class)) {
7881
/*
7982
* Synthetic methods are usually methods injected by javac to provide access to

substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/ReflectionUtil.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.lang.reflect.Field;
3131
import java.lang.reflect.InvocationTargetException;
3232
import java.lang.reflect.Method;
33+
import java.util.function.Function;
3334

3435
/**
3536
* This class contains utility methods for commonly used reflection functionality. Note that lookups
@@ -86,7 +87,7 @@ public static Method lookupMethod(boolean optional, Class<?> declaringClass, Str
8687
openModule(declaringClass);
8788
result.setAccessible(true);
8889
return result;
89-
} catch (ReflectiveOperationException ex) {
90+
} catch (ReflectiveOperationException | LinkageError ex) {
9091
if (optional) {
9192
return null;
9293
}
@@ -238,4 +239,17 @@ public static void writeField(Class<?> declaringClass, String fieldName, Object
238239
public static void writeStaticField(Class<?> declaringClass, String fieldName, Object value) {
239240
writeField(declaringClass, fieldName, null, value);
240241
}
242+
243+
/**
244+
* Query a class value without worrying if the class can be properly linked.
245+
*/
246+
public static <T> T linkageSafeQuery(Class<?> clazz, T defaultValue, Function<Class<?>, T> f) {
247+
var result = defaultValue;
248+
try {
249+
result = f.apply(clazz);
250+
} catch (LinkageError e) {
251+
/* nothing we can do */
252+
}
253+
return result;
254+
}
241255
}

0 commit comments

Comments
 (0)