package net.bytebuddy.implementation.attribute; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.asm.AsmVisitorWrapper; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy; import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import org.junit.rules.TestRule; import org.mockito.Mock; import org.objectweb.asm.*; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.ProtectionDomain; import java.util.Collections; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class AnnotationAppenderDefaultTest { private static final String BAR = "net.bytebuddy.test.Bar"; private static final String FOOBAR = "foobar"; @Rule public TestRule mockitoRule = new MockitoRule(this); @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Mock private AnnotationAppender.Target target; @Mock private AnnotationValueFilter valueFilter; @Mock private Retention retention; private AnnotationAppender annotationAppender; @Before public void setUp() throws Exception { annotationAppender = new AnnotationAppender.Default(target); } @Test public void testNoArgumentAnnotation() throws Exception { Class<?> bar = makeTypeWithAnnotation(new Foo.Instance()); assertThat(bar.getAnnotations().length, is(1)); assertThat(bar.isAnnotationPresent(Foo.class), is(true)); } @Test public void testNoArgumentAnnotationSourceCodeRetention() throws Exception { Class<?> bar = makeTypeWithAnnotation(new FooSourceCodeRetention.Instance()); assertThat(bar.getAnnotations().length, is(0)); } @Test public void testNoArgumentAnnotationByteCodeRetention() throws Exception { Class<?> bar = makeTypeWithAnnotation(new FooByteCodeRetention.Instance()); assertThat(bar.getAnnotations().length, is(0)); } @Test public void testNoArgumentAnnotationNoRetention() throws Exception { Class<?> bar = makeTypeWithAnnotation(new FooNoRetention.Instance()); assertThat(bar.getAnnotations().length, is(0)); } @Test public void testSingleArgumentAnnotation() throws Exception { Class<?> bar = makeTypeWithAnnotation(new Qux.Instance(FOOBAR)); assertThat(bar.getAnnotations().length, is(1)); assertThat(bar.isAnnotationPresent(Qux.class), is(true)); assertThat(bar.getAnnotation(Qux.class).value(), is(FOOBAR)); } @Test public void testMultipleArgumentAnnotation() throws Exception { int[] array = {2, 3, 4}; Class<?> bar = makeTypeWithAnnotation(new Baz.Instance(FOOBAR, array, new Foo.Instance(), Baz.Enum.VALUE, Void.class)); assertThat(bar.getAnnotations().length, is(1)); assertThat(bar.isAnnotationPresent(Baz.class), is(true)); assertThat(bar.getAnnotation(Baz.class).value(), is(FOOBAR)); assertThat(bar.getAnnotation(Baz.class).array(), is(array)); assertThat(bar.getAnnotation(Baz.class).annotation(), is((Foo) new Foo.Instance())); assertThat(bar.getAnnotation(Baz.class).enumeration(), is(Baz.Enum.VALUE)); assertThat(bar.getAnnotation(Baz.class).type(), CoreMatchers.<Class<?>>is(Void.class)); } @Test @JavaVersionRule.Enforce(8) public void testNoArgumentTypeAnnotation() throws Exception { Class<?> bar = makeTypeWithSuperClassAnnotation(new Foo.Instance()); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(1)); assertThat(annotationReader.asList().isAnnotationPresent(Foo.class), is(true)); } @Test @JavaVersionRule.Enforce(8) public void testNoArgumentTypeAnnotationSourceCodeRetention() throws Exception { Class<?> bar = makeTypeWithSuperClassAnnotation(new FooSourceCodeRetention.Instance()); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(0)); } @Test @JavaVersionRule.Enforce(8) public void testNoArgumentTypeAnnotationByteCodeRetention() throws Exception { Class<?> bar = makeTypeWithSuperClassAnnotation(new FooByteCodeRetention.Instance()); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(0)); } @Test @JavaVersionRule.Enforce(8) public void testNoArgumentTypeAnnotationNoRetention() throws Exception { Class<?> bar = makeTypeWithSuperClassAnnotation(new FooNoRetention.Instance()); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(0)); } @Test @JavaVersionRule.Enforce(8) public void testSingleTypeArgumentAnnotation() throws Exception { Class<?> bar = makeTypeWithSuperClassAnnotation(new Qux.Instance(FOOBAR)); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(1)); assertThat(annotationReader.asList().isAnnotationPresent(Qux.class), is(true)); assertThat(annotationReader.asList().ofType(Qux.class).load().value(), is(FOOBAR)); } @Test @JavaVersionRule.Enforce(8) public void testMultipleTypeArgumentAnnotation() throws Exception { int[] array = {2, 3, 4}; Class<?> bar = makeTypeWithSuperClassAnnotation(new Baz.Instance(FOOBAR, array, new Foo.Instance(), Baz.Enum.VALUE, Void.class)); TypeDescription.Generic.AnnotationReader annotationReader = TypeDescription.Generic.AnnotationReader.DISPATCHER.resolveSuperClassType(bar); assertThat(annotationReader.asList().size(), is(1)); assertThat(annotationReader.asList().isAnnotationPresent(Baz.class), is(true)); assertThat(annotationReader.asList().ofType(Baz.class).load().value(), is(FOOBAR)); assertThat(annotationReader.asList().ofType(Baz.class).load().array(), is(array)); assertThat(annotationReader.asList().ofType(Baz.class).load().annotation(), is((Foo) new Foo.Instance())); assertThat(annotationReader.asList().ofType(Baz.class).load().enumeration(), is(Baz.Enum.VALUE)); assertThat(annotationReader.asList().ofType(Baz.class).load().type(), CoreMatchers.<Class<?>>is(Void.class)); } private Class<?> makeTypeWithAnnotation(Annotation annotation) throws Exception { when(valueFilter.isRelevant(any(AnnotationDescription.class), any(MethodDescription.InDefinedShape.class))).thenReturn(true); ClassWriter classWriter = new ClassWriter(AsmVisitorWrapper.NO_FLAGS); classWriter.visit(ClassFileVersion.ofThisVm().getMinorMajorVersion(), Opcodes.ACC_PUBLIC, BAR.replace('.', '/'), null, Type.getInternalName(Object.class), null); AnnotationVisitor annotationVisitor = classWriter.visitAnnotation(Type.getDescriptor(annotation.annotationType()), true); when(target.visit(any(String.class), anyBoolean())).thenReturn(annotationVisitor); AnnotationDescription annotationDescription = AnnotationDescription.ForLoadedAnnotation.of(annotation); annotationAppender.append(annotationDescription, valueFilter); classWriter.visitEnd(); Class<?> bar = new ByteArrayClassLoader(getClass().getClassLoader(), Collections.singletonMap(BAR, classWriter.toByteArray())).loadClass(BAR); assertThat(bar.getName(), is(BAR)); assertThat(bar.getSuperclass(), CoreMatchers.<Class<?>>is(Object.class)); return bar; } private Class<?> makeTypeWithSuperClassAnnotation(Annotation annotation) throws Exception { when(valueFilter.isRelevant(any(AnnotationDescription.class), any(MethodDescription.InDefinedShape.class))).thenReturn(true); ClassWriter classWriter = new ClassWriter(AsmVisitorWrapper.NO_FLAGS); classWriter.visit(ClassFileVersion.ofThisVm().getMinorMajorVersion(), Opcodes.ACC_PUBLIC, BAR.replace('.', '/'), null, Type.getInternalName(Object.class), null); AnnotationVisitor annotationVisitor = classWriter.visitTypeAnnotation(TypeReference.newSuperTypeReference(-1).getValue(), null, Type.getDescriptor(annotation.annotationType()), true); when(target.visit(any(String.class), anyBoolean())).thenReturn(annotationVisitor); AnnotationDescription annotationDescription = AnnotationDescription.ForLoadedAnnotation.of(annotation); annotationAppender.append(annotationDescription, valueFilter); classWriter.visitEnd(); Class<?> bar = new ByteArrayClassLoader(getClass().getClassLoader(), Collections.singletonMap(BAR, classWriter.toByteArray())).loadClass(BAR); assertThat(bar.getName(), is(BAR)); assertThat(bar.getSuperclass(), CoreMatchers.<Class<?>>is(Object.class)); return bar; } @Test public void testSourceRetentionAnnotation() throws Exception { AnnotationVisitor annotationVisitor = mock(AnnotationVisitor.class); when(target.visit(anyString(), anyBoolean())).thenReturn(annotationVisitor); AnnotationDescription annotationDescription = mock(AnnotationDescription.class); when(annotationDescription.getRetention()).thenReturn(RetentionPolicy.SOURCE); annotationAppender.append(annotationDescription, valueFilter); verifyZeroInteractions(valueFilter); verifyZeroInteractions(annotationVisitor); } @Test public void testSourceRetentionTypeAnnotation() throws Exception { AnnotationVisitor annotationVisitor = mock(AnnotationVisitor.class); when(target.visit(anyString(), anyBoolean())).thenReturn(annotationVisitor); AnnotationDescription annotationDescription = mock(AnnotationDescription.class); when(annotationDescription.getRetention()).thenReturn(RetentionPolicy.SOURCE); annotationAppender.append(annotationDescription, valueFilter, 0, null); verifyZeroInteractions(valueFilter); verifyZeroInteractions(annotationVisitor); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(AnnotationAppender.Default.class).apply(); } @Retention(RetentionPolicy.RUNTIME) public @interface Foo { @SuppressWarnings("all") class Instance implements Foo { @Override public Class<? extends Annotation> annotationType() { return Foo.class; } } } @Retention(RetentionPolicy.SOURCE) public @interface FooSourceCodeRetention { @SuppressWarnings("all") class Instance implements FooSourceCodeRetention { @Override public Class<? extends Annotation> annotationType() { return FooSourceCodeRetention.class; } } } @Retention(RetentionPolicy.CLASS) public @interface FooByteCodeRetention { @SuppressWarnings("all") class Instance implements FooByteCodeRetention { @Override public Class<? extends Annotation> annotationType() { return FooByteCodeRetention.class; } } } public @interface FooNoRetention { @SuppressWarnings("all") class Instance implements FooNoRetention { @Override public Class<? extends Annotation> annotationType() { return FooNoRetention.class; } } } @Retention(RetentionPolicy.RUNTIME) public @interface Qux { String value(); @SuppressWarnings("all") class Instance implements Qux { private final String value; public Instance(String value) { this.value = value; } @Override public String value() { return value; } @Override public Class<? extends Annotation> annotationType() { return Qux.class; } } } @Retention(RetentionPolicy.RUNTIME) public @interface Baz { String value(); int[] array(); Foo annotation(); Enum enumeration(); Class<?> type(); enum Enum { VALUE } @SuppressWarnings("all") class Instance implements Baz { private final String value; private final int[] array; private final Foo annotation; private final Enum enumeration; private final Class<?> type; public Instance(String value, int[] array, Foo annotation, Enum enumeration, Class<?> type) { this.value = value; this.array = array; this.annotation = annotation; this.enumeration = enumeration; this.type = type; } @Override public String value() { return value; } @Override public int[] array() { return array; } @Override public Foo annotation() { return annotation; } @Override public Enum enumeration() { return enumeration; } @Override public Class<?> type() { return type; } @Override public Class<? extends Annotation> annotationType() { return Baz.class; } } } }