Skip to content

Commit

Permalink
[GR-61279] Fixes for various linkage errors.
Browse files Browse the repository at this point in the history
PullRequest: graal/19794
  • Loading branch information
vjovanov committed Jan 16, 2025
2 parents c574793 + ccb514b commit 826b2b6
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,20 @@ public void checkType(ResolvedJavaType type, AnalysisUniverse universe) {
throw new UnsupportedFeatureException(message);
}
if (originalClass.isRecord()) {
for (var recordComponent : originalClass.getRecordComponents()) {
if (WordBase.class.isAssignableFrom(recordComponent.getType())) {
throw UserError.abort("Records cannot use Word types. " +
"The equals/hashCode/toString implementation of records uses method handles, and Word types are not supported as parameters of method handle invocations. " +
"Record type: `" + originalClass.getTypeName() + "`, component: `" + recordComponent.getName() + "` of type `" + recordComponent.getType().getTypeName() + "`");
try {
for (var recordComponent : originalClass.getRecordComponents()) {
if (WordBase.class.isAssignableFrom(recordComponent.getType())) {
throw UserError.abort("Records cannot use Word types. " +
"The equals/hashCode/toString implementation of records uses method handles, and Word types are not supported as parameters of method handle invocations. " +
"Record type: `" + originalClass.getTypeName() + "`, component: `" + recordComponent.getName() + "` of type `" + recordComponent.getType().getTypeName() + "`");
}
}
} catch (LinkageError e) {
/*
* If a record refers to a missing/incomplete type then Class.getRecordComponents()
* will throw a LinkageError. It's safe to ignore this here since the Word type
* restriction applies to VM classes which should be fully defined.
*/
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public RuntimeMetadataEncoder create(SnippetReflectionProvider snippetReflection
private final Map<HostedType, Throwable> fieldLookupErrors = new HashMap<>();
private final Map<HostedType, Throwable> methodLookupErrors = new HashMap<>();
private final Map<HostedType, Throwable> constructorLookupErrors = new HashMap<>();
private final Map<HostedType, Throwable> recordComponentLookupErrors = new HashMap<>();

private final Set<AccessibleObjectMetadata> heapData = new HashSet<>();

Expand Down Expand Up @@ -684,6 +685,13 @@ public void addConstructorLookupError(HostedType declaringClass, Throwable excep
constructorLookupErrors.put(declaringClass, exception);
}

@Override
public void addRecordComponentsLookupError(HostedType declaringClass, Throwable exception) {
addType(declaringClass);
registerError(exception);
recordComponentLookupErrors.put(declaringClass, exception);
}

private static HostedType[] getParameterTypes(HostedMethod method) {
HostedType[] parameterTypes = new HostedType[method.getSignature().getParameterCount(false)];
for (int i = 0; i < parameterTypes.length; ++i) {
Expand Down Expand Up @@ -784,7 +792,7 @@ public void encodeAllAndInstall() {
int fieldsIndex = encodeAndAddCollection(buf, getFields(declaringType), fieldLookupErrors.get(declaringType), this::encodeField, false);
int methodsIndex = encodeAndAddCollection(buf, getMethods(declaringType), methodLookupErrors.get(declaringType), this::encodeExecutable, false);
int constructorsIndex = encodeAndAddCollection(buf, getConstructors(declaringType), constructorLookupErrors.get(declaringType), this::encodeExecutable, false);
int recordComponentsIndex = encodeAndAddCollection(buf, classMetadata.recordComponents, this::encodeRecordComponent, true);
int recordComponentsIndex = encodeAndAddCollection(buf, classMetadata.recordComponents, recordComponentLookupErrors.get(declaringType), this::encodeRecordComponent, true);
int classFlags = classMetadata.flags;
if (anySet(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex) || classFlags != hub.getModifiers()) {
hub.setReflectionMetadata(fieldsIndex, methodsIndex, constructorsIndex, recordComponentsIndex, classFlags);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ protected void buildRuntimeMetadata(DebugContext debug, SnippetReflectionProvide
runtimeMetadataEncoder.addConstructorLookupError(type, error);
});

reflectionSupport.getRecordComponentLookupErrors().forEach((clazz, error) -> {
HostedType type = hMetaAccess.lookupJavaType(clazz);
runtimeMetadataEncoder.addRecordComponentsLookupError(type, error);
});

Set<AnalysisField> includedFields = new HashSet<>();
Set<AnalysisMethod> includedMethods = new HashSet<>();
Map<AnalysisType, Map<AnalysisField, ConditionalRuntimeValue<Field>>> configurationFields = reflectionSupport.getReflectionFields();
Expand Down Expand Up @@ -831,6 +836,8 @@ public interface RuntimeMetadataEncoder extends EncodedRuntimeMetadataSupplier {

void addConstructorLookupError(HostedType declaringClass, Throwable exception);

void addRecordComponentsLookupError(HostedType declaringClass, Throwable exception);

void encodeAllAndInstall();

Method getRoot = ReflectionUtil.lookupMethod(AccessibleObject.class, "getRoot");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl
private final Map<Class<?>, Throwable> fieldLookupExceptions = new ConcurrentHashMap<>();
private final Map<Class<?>, Throwable> methodLookupExceptions = new ConcurrentHashMap<>();
private final Map<Class<?>, Throwable> constructorLookupExceptions = new ConcurrentHashMap<>();
private final Map<Class<?>, Throwable> recordComponentsLookupExceptions = new ConcurrentHashMap<>();

// Intermediate bookkeeping
private final Map<Type, Set<Integer>> processedTypes = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -501,6 +502,8 @@ public void registerMethodLookup(ConfigurationCondition condition, Class<?> decl
} catch (NoSuchMethodException e) {
negativeMethodLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
.add(new AnalysisMethod.Signature(methodName, metaAccess.lookupJavaTypes(parameterTypes)));
} catch (LinkageError le) {
registerLinkageError(declaringClass, le, methodLookupExceptions);
}
});
}
Expand All @@ -514,6 +517,8 @@ public void registerConstructorLookup(ConfigurationCondition condition, Class<?>
} catch (NoSuchMethodException e) {
negativeConstructorLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet())
.add(metaAccess.lookupJavaTypes(parameterTypes));
} catch (LinkageError le) {
registerLinkageError(declaringClass, le, constructorLookupExceptions);
}
});
}
Expand Down Expand Up @@ -611,7 +616,7 @@ private void registerField(ConfigurationCondition cnd, boolean queriedOnly, Fiel
}

/*
* We need to run this even if the method has already been registered, in case it was only
* We need to run this even if the field has already been registered, in case it was only
* registered as queried.
*/
if (!queriedOnly) {
Expand All @@ -631,6 +636,8 @@ public void registerFieldLookup(ConfigurationCondition condition, Class<?> decla
* not necessary.
*/
negativeFieldLookups.computeIfAbsent(metaAccess.lookupJavaType(declaringClass), (key) -> ConcurrentHashMap.newKeySet()).add(fieldName);
} catch (LinkageError le) {
registerLinkageError(declaringClass, le, fieldLookupExceptions);
}
});
}
Expand Down Expand Up @@ -752,14 +759,18 @@ private void registerTypesForClass(AnalysisType analysisType, Class<?> clazz) {
}

private void registerRecordComponents(Class<?> clazz) {
RecordComponent[] recordComponents = clazz.getRecordComponents();
if (recordComponents == null) {
return;
}
for (RecordComponent recordComponent : recordComponents) {
registerTypesForRecordComponent(recordComponent);
try {
RecordComponent[] recordComponents = clazz.getRecordComponents();
if (recordComponents == null) {
return;
}
for (RecordComponent recordComponent : recordComponents) {
registerTypesForRecordComponent(recordComponent);
}
registeredRecordComponents.put(clazz, recordComponents);
} catch (LinkageError le) {
registerLinkageError(clazz, le, recordComponentsLookupExceptions);
}
registeredRecordComponents.put(clazz, recordComponents);
}

private void registerTypesForEnclosingMethodInfo(Class<?> clazz) {
Expand Down Expand Up @@ -1080,20 +1091,24 @@ private void maybeRegisterRecordComponents(Class<?> clazz) {
* components in that case will throw an exception at image run time, see
* DynamicHub.getRecordComponents0().
*/
Method[] accessors = RecordUtils.getRecordComponentAccessorMethods(clazz);
Set<Method> unregisteredAccessors = ConcurrentHashMap.newKeySet();
for (Method accessor : accessors) {
if (SubstitutionReflectivityFilter.shouldExclude(accessor, metaAccess, universe)) {
return;
try {
Method[] accessors = RecordUtils.getRecordComponentAccessorMethods(clazz);
Set<Method> unregisteredAccessors = ConcurrentHashMap.newKeySet();
for (Method accessor : accessors) {
if (SubstitutionReflectivityFilter.shouldExclude(accessor, metaAccess, universe)) {
return;
}
unregisteredAccessors.add(accessor);
}
unregisteredAccessors.add(accessor);
}
pendingRecordClasses.put(clazz, unregisteredAccessors);
pendingRecordClasses.put(clazz, unregisteredAccessors);

AnalysisType analysisType = metaAccess.lookupJavaType(clazz);
unregisteredAccessors.removeIf(accessor -> registeredMethods.getOrDefault(analysisType, Collections.emptyMap()).containsKey(metaAccess.lookupJavaMethod(accessor)));
if (unregisteredAccessors.isEmpty()) {
registerRecordComponents(clazz);
AnalysisType analysisType = metaAccess.lookupJavaType(clazz);
unregisteredAccessors.removeIf(accessor -> registeredMethods.getOrDefault(analysisType, Collections.emptyMap()).containsKey(metaAccess.lookupJavaMethod(accessor)));
if (unregisteredAccessors.isEmpty()) {
registerRecordComponents(clazz);
}
} catch (LinkageError le) {
registerLinkageError(clazz, le, recordComponentsLookupExceptions);
}
}

Expand Down Expand Up @@ -1280,6 +1295,11 @@ public Map<Class<?>, Throwable> getConstructorLookupErrors() {
return Collections.unmodifiableMap(constructorLookupExceptions);
}

@Override
public Map<Class<?>, Throwable> getRecordComponentLookupErrors() {
return Collections.unmodifiableMap(recordComponentsLookupExceptions);
}

private static final AnnotationValue[] NO_ANNOTATIONS = new AnnotationValue[0];

public AnnotationValue[] getAnnotationData(AnnotatedElement element) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public interface ReflectionHostedSupport {

Map<Class<?>, Throwable> getConstructorLookupErrors();

Map<Class<?>, Throwable> getRecordComponentLookupErrors();

int getReflectionMethodsCount();

int getReflectionFieldsCount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,11 +527,17 @@ private void registerForSerialization(ConfigurationCondition cnd, Class<?> seria

Class<?> iter = serializationTargetClass;
while (iter != null) {
Arrays.stream(iter.getDeclaredFields()).map(Field::getType).forEach(type -> {
RuntimeReflection.registerAllDeclaredMethods(type);
RuntimeReflection.registerAllDeclaredFields(type);
RuntimeReflection.registerAllDeclaredConstructors(type);
});
RuntimeReflection.registerAllDeclaredFields(iter);
try {
Arrays.stream(iter.getDeclaredFields())
.map(Field::getType).forEach(type -> {
RuntimeReflection.registerAllDeclaredMethods(type);
RuntimeReflection.registerAllDeclaredFields(type);
RuntimeReflection.registerAllDeclaredConstructors(type);
});
} catch (LinkageError l) {
/* Handled with registration above */
}
iter = iter.getSuperclass();
}
}
Expand All @@ -549,10 +555,14 @@ static void registerSerializationUIDElements(Class<?> serializationTargetClass,
RuntimeReflection.registerAllDeclaredMethods(serializationTargetClass);
RuntimeReflection.registerAllDeclaredFields(serializationTargetClass);
if (fullyRegister) {
/* This is here a legacy that we can't remove as it is a breaking change */
RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
try {
/* This is here a legacy that we can't remove as it is a breaking change */
RuntimeReflection.register(serializationTargetClass.getDeclaredConstructors());
RuntimeReflection.register(serializationTargetClass.getDeclaredMethods());
RuntimeReflection.register(serializationTargetClass.getDeclaredFields());
} catch (LinkageError e) {
/* Handled by registrations above */
}
}
RuntimeReflection.registerFieldLookup(serializationTargetClass, "serialPersistentFields");
}
Expand All @@ -565,17 +575,23 @@ private static void registerForDeserialization(ConfigurationCondition cnd, Class
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, serializationTargetClass);

if (serializationTargetClass.isRecord()) {
/* Serialization for records uses the canonical record constructor directly. */
Executable[] methods = new Executable[]{RecordUtils.getCanonicalRecordConstructor(serializationTargetClass)};
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods);
/*
* Serialization for records invokes Class.getRecordComponents(). Registering all record
* component accessor methods for reflection ensures that the record components are
* available at run time.
*/
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerAllRecordComponentsQuery(cnd, serializationTargetClass);
Executable[] methods1 = RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass);
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods1);
try {
/* Serialization for records uses the canonical record constructor directly. */
Executable[] methods = new Executable[]{RecordUtils.getCanonicalRecordConstructor(serializationTargetClass)};
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods);
Executable[] methods1 = RecordUtils.getRecordComponentAccessorMethods(serializationTargetClass);
ImageSingletons.lookup(RuntimeReflectionSupport.class).register(cnd, false, methods1);
} catch (LinkageError le) {
/*
* Handled by the record component registration above.
*/
}
} else if (Externalizable.class.isAssignableFrom(serializationTargetClass)) {
RuntimeReflection.registerConstructorLookup(serializationTargetClass);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static Method lookupMethod(boolean optional, Class<?> declaringClass, Str
openModule(declaringClass);
result.setAccessible(true);
return result;
} catch (ReflectiveOperationException ex) {
} catch (ReflectiveOperationException | LinkageError ex) {
if (optional) {
return null;
}
Expand Down

0 comments on commit 826b2b6

Please sign in to comment.