Skip to content
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 @@ -20,7 +20,6 @@
import java.lang.classfile.AnnotationElement;
import java.lang.classfile.AnnotationValue;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.constant.ClassDesc;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -63,7 +62,7 @@ static MergedAnnotations createMergedAnnotations(
private static <A extends java.lang.annotation.Annotation> @Nullable MergedAnnotation<A> createMergedAnnotation(
String className, Annotation annotation, @Nullable ClassLoader classLoader) {

String typeName = fromTypeDescriptor(annotation.className().stringValue());
String typeName = ClassFileAnnotationMetadata.resolveTypeName(annotation.classSymbol());
if (AnnotationFilter.PLAIN.matches(typeName)) {
return null;
}
Expand Down Expand Up @@ -97,7 +96,7 @@ static MergedAnnotations createMergedAnnotations(
return createMergedAnnotation(className, annotationValue.annotation(), classLoader);
}
case AnnotationValue.OfClass classValue -> {
return fromTypeDescriptor(classValue.className().stringValue());
return ClassFileAnnotationMetadata.resolveTypeName(classValue.classSymbol());
}
case AnnotationValue.OfEnum enumValue -> {
return parseEnum(enumValue, classLoader);
Expand All @@ -108,12 +107,6 @@ static MergedAnnotations createMergedAnnotations(
}
}

private static String fromTypeDescriptor(String descriptor) {
ClassDesc classDesc = ClassDesc.ofDescriptor(descriptor);
return (classDesc.isPrimitive() ? classDesc.displayName() :
classDesc.packageName() + "." + classDesc.displayName());
}

private static Object parseArrayValue(String className, @Nullable ClassLoader classLoader, AnnotationValue.OfArray arrayValue) {
if (arrayValue.values().isEmpty()) {
return new Object[0];
Expand Down Expand Up @@ -145,7 +138,7 @@ private static <E extends Enum<E>> Enum<E> parseEnum(AnnotationValue.OfEnum enum
}

private static Class<?> loadEnumClass(AnnotationValue.OfEnum enumValue, @Nullable ClassLoader classLoader) {
String className = fromTypeDescriptor(enumValue.className().stringValue());
String className = ClassFileAnnotationMetadata.resolveTypeName(enumValue.classSymbol());
return ClassUtils.resolveClassName(className, classLoader);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.lang.classfile.attribute.NestHostAttribute;
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.constant.ClassDesc;
import java.lang.reflect.AccessFlag;
import java.util.Collections;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -221,6 +222,17 @@ static ClassFileAnnotationMetadata of(ClassModel classModel, @Nullable ClassLoad
return builder.build();
}

static String resolveTypeName(ClassDesc type) {
if (type.isPrimitive()) {
return type.displayName();
}
if (type.isArray()) {
return resolveTypeName(type.componentType()) + "[]";
}
String packageName = type.packageName();
return (packageName.isEmpty() ? type.displayName() : packageName + "." + type.displayName());
}


static class Builder {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.lang.reflect.AccessFlag;
import java.util.Collections;
import java.util.Locale;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -143,7 +144,7 @@ static ClassFileMethodMetadata of(MethodModel methodModel, ClassLoader classLoad
AccessFlags flags = methodModel.flags();
String declaringClassName = methodModel.parent().map(parent -> ClassUtils.convertResourcePathToClassName(parent.thisClass().name().stringValue())).orElse(null);
ClassDesc returnType = methodModel.methodTypeSymbol().returnType();
String returnTypeName = returnType.packageName() + "." + returnType.displayName();
String returnTypeName = ClassFileAnnotationMetadata.resolveTypeName(returnType);
Source source = new Source(declaringClassName, flags, methodName, methodModel.methodTypeSymbol());
MergedAnnotations annotations = methodModel.elementStream()
.filter(element -> element instanceof RuntimeVisibleAnnotationsAttribute)
Expand All @@ -163,16 +164,30 @@ static ClassFileMethodMetadata of(MethodModel methodModel, ClassLoader classLoad
*/
record Source(@Nullable String declaringClassName, AccessFlags flags, String methodName, MethodTypeDesc descriptor) {

@Override
public boolean equals(Object o) {
if (!(o instanceof Source source)) {
return false;
}
return Objects.equals(this.flags.flagsMask(), source.flags.flagsMask()) &&
Objects.equals(this.methodName, source.methodName) &&
Objects.equals(this.declaringClassName, source.declaringClassName) &&
Objects.equals(this.descriptor.descriptorString(), source.descriptor.descriptorString());
}

@Override
public int hashCode() {
return Objects.hash(this.declaringClassName, this.flags.flagsMask(), this.methodName, this.descriptor.descriptorString());
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
this.flags.flags().forEach(flag -> {
builder.append(flag.name().toLowerCase(Locale.ROOT));
builder.append(' ');
});
builder.append(this.descriptor.returnType().packageName());
builder.append(".");
builder.append(this.descriptor.returnType().displayName());
builder.append(ClassFileAnnotationMetadata.resolveTypeName(this.descriptor.returnType()));
builder.append(' ');
builder.append(this.declaringClassName);
builder.append('.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

/**
* Base class for {@link MethodMetadata} tests.
Expand Down Expand Up @@ -76,13 +77,19 @@ void verifyHashCode() {
@Test
void verifyToString() {
assertThat(getTagged(WithMethod.class).toString())
.endsWith(WithMethod.class.getName() + ".test()");
.isEqualTo("public java.lang.String " + WithMethod.class.getName() + ".test()");

assertThat(getTagged(WithMethodWithOneArgument.class).toString())
.endsWith(WithMethodWithOneArgument.class.getName() + ".test(java.lang.String)");
.isEqualTo("public java.lang.String " + WithMethodWithOneArgument.class.getName() + ".test(java.lang.String)");

assertThat(getTagged(WithMethodWithTwoArguments.class).toString())
.endsWith(WithMethodWithTwoArguments.class.getName() + ".test(java.lang.String,java.lang.Integer)");
.isEqualTo("public java.lang.String " + WithMethodWithTwoArguments.class.getName() + ".test(java.lang.String,java.lang.Integer)");

assertThat(getTagged(WithPrimitiveArrayMethod.class).toString())
.isEqualTo("public int[] " + WithPrimitiveArrayMethod.class.getName() + ".test()");

assertThat(getTagged(WithStringArrayMethod.class).toString())
.isEqualTo("public java.lang.String[] " + WithStringArrayMethod.class.getName() + ".test()");
}

@Test
Expand All @@ -102,6 +109,39 @@ void getReturnTypeReturnsReturnType() {
String.class.getName());
}

@Test
void getReturnTypeReturnsVoidForVoidReturnType() {
assertThat(getTagged(WithVoidMethod.class).getReturnTypeName()).isEqualTo("void");
}

@Test
void getReturnTypeReturnsPrimitiveArrayForPrimitiveArrayReturnTypeForStandardReflection() {
MethodMetadata methodMetadata = getTagged(WithPrimitiveArrayMethod.class);
assumeTrue(methodMetadata instanceof StandardMethodMetadata, "skipped for ASM and ClassFile");
assertThat(methodMetadata.getReturnTypeName()).isEqualTo("[I");
}

@Test
void getReturnTypeReturnsPrimitiveArrayForPrimitiveArrayReturnType() {
MethodMetadata methodMetadata = getTagged(WithPrimitiveArrayMethod.class);
assumeTrue(!(methodMetadata instanceof StandardMethodMetadata), "skipped for standard reflection");
assertThat(methodMetadata.getReturnTypeName()).isEqualTo("int[]");
}

@Test
void getReturnTypeReturnsStringArrayForStringArrayReturnTypeForStandardReflection() {
MethodMetadata methodMetadata = getTagged(WithStringArrayMethod.class);
assumeTrue(methodMetadata instanceof StandardMethodMetadata, "skipped for ASM and ClassFile");
assertThat(methodMetadata.getReturnTypeName()).isEqualTo("[Ljava.lang.String;");
}

@Test
void getReturnTypeReturnsStringArrayForStringArrayReturnType() {
MethodMetadata methodMetadata = getTagged(WithStringArrayMethod.class);
assumeTrue(!(methodMetadata instanceof StandardMethodMetadata), "skipped for standard reflection");
assertThat(methodMetadata.getReturnTypeName()).isEqualTo("java.lang.String[]");
}

@Test
void isAbstractWhenAbstractReturnsTrue() {
assertThat(getTagged(WithAbstractMethod.class).isAbstract()).isTrue();
Expand Down Expand Up @@ -217,6 +257,27 @@ public String test() {

}

public static class WithVoidMethod {

@Tag
public void test() {}

}

public static class WithPrimitiveArrayMethod {

@Tag
public int[] test() { return new int[0];}

}

public static class WithStringArrayMethod {

@Tag
public String[] test() { return new String[0];}

}

public static class WithMethodWithOneArgument {

@Tag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,30 @@

package org.springframework.core.type.classreading;

import java.io.IOException;

import org.springframework.core.type.AbstractMethodMetadataTests;
import org.springframework.core.type.AnnotationMetadata;


/**
* Tests for {@link SimpleMethodMetadata} and
* {@link SimpleMethodMetadataReadingVisitor}.
* {@link SimpleMethodMetadataReadingVisitor} on Java < 24,
* and for the ClassFile API variant on Java >= 24.
*
* @author Phillip Webb
* @author Brian Clozel
*/
class SimpleMethodMetadataTests extends AbstractMethodMetadataTests {
class DefaultMethodMetadataTests extends AbstractMethodMetadataTests {


@Override
protected AnnotationMetadata get(Class<?> source) {
try {
return new SimpleMetadataReaderFactory(
source.getClassLoader()).getMetadataReader(
source.getName()).getAnnotationMetadata();
return MetadataReaderFactory.create(source.getClassLoader())
.getMetadataReader(source.getName()).getAnnotationMetadata();
}
catch (Exception ex) {
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
Expand Down
Loading