mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Fix enclosing class resolution with ClassFile API
Prior to this commit, the `ClassFile` based implementation of `AnnotationMetadata` would rely on the `NestHost` class element to get the enclosing class name for a nested class. This approach works for bytecode emitted by Java11+, which aligns with our Java17+ runtime policy. But there are cases where bytecode was not emitted by a Java11+ compiler, such as Kotlin. In this case, the `NestHost` class element is absent and we should instead use the `InnerClasses` information to get it. This commit makes use of `InnerClasses` to get the enclosing class name, but still uses `NestHost` as a fallback for anonymous classes. Fixes gh-36451
This commit is contained in:
+11
-4
@@ -195,7 +195,7 @@ final class ClassFileAnnotationMetadata implements AnnotationMetadata {
|
||||
builder.accessFlags(flags);
|
||||
}
|
||||
case NestHostAttribute _ -> {
|
||||
builder.enclosingClass(classModel.thisClass());
|
||||
builder.enclosingClassFromNestHost(classModel.thisClass());
|
||||
}
|
||||
case InnerClassesAttribute innerClasses -> {
|
||||
builder.nestMembers(currentClassName, innerClasses);
|
||||
@@ -256,11 +256,16 @@ final class ClassFileAnnotationMetadata implements AnnotationMetadata {
|
||||
this.accessFlags = accessFlags;
|
||||
}
|
||||
|
||||
void enclosingClass(ClassEntry thisClass) {
|
||||
void enclosingClassFromNestHost(ClassEntry thisClass) {
|
||||
if (this.enclosingClassName != null) {
|
||||
return;
|
||||
}
|
||||
String thisClassName = thisClass.name().stringValue();
|
||||
int currentClassIndex = thisClassName.lastIndexOf('$');
|
||||
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(
|
||||
thisClassName.substring(0, currentClassIndex));
|
||||
if (currentClassIndex > 0) {
|
||||
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(
|
||||
thisClassName.substring(0, currentClassIndex));
|
||||
}
|
||||
}
|
||||
|
||||
void superClass(Superclass superClass) {
|
||||
@@ -280,6 +285,8 @@ final class ClassFileAnnotationMetadata implements AnnotationMetadata {
|
||||
if (currentClassName.equals(innerClassName)) {
|
||||
// the current class is an inner class
|
||||
this.innerAccessFlags = classInfo.flags();
|
||||
classInfo.outerClass().ifPresent(outerClass ->
|
||||
this.enclosingClassName = ClassUtils.convertResourcePathToClassName(outerClass.name().stringValue()));
|
||||
}
|
||||
classInfo.outerClass().ifPresent(outerClass -> {
|
||||
if (outerClass.name().stringValue().equals(currentClassName)) {
|
||||
|
||||
+8
@@ -148,6 +148,14 @@ public abstract class AbstractAnnotationMetadataTests {
|
||||
assertThat(get(AbstractAnnotationMetadataTests.class).getEnclosingClassName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnclosingClassNameWhenNestedMemberClassReturnsImmediateEnclosingClass() {
|
||||
assertThat(get(TestNestedMemberClass.TestMemberClassInnerClassA.class).getEnclosingClassName())
|
||||
.isEqualTo(TestNestedMemberClass.class.getName());
|
||||
assertThat(get(TestNestedMemberClass.TestMemberClassInnerClassA.TestMemberClassInnerClassAA.class).getEnclosingClassName())
|
||||
.isEqualTo(TestNestedMemberClass.TestMemberClassInnerClassA.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuperClassNameWhenHasSuperClassReturnsName() {
|
||||
assertThat(get(TestSubclass.class).getSuperClassName()).isEqualTo(TestClass.class.getName());
|
||||
|
||||
Reference in New Issue
Block a user