Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-61279] Fixes for various linkage errors. #10482

Merged
merged 1 commit into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading