package net.bytebuddy.dynamic; import net.bytebuddy.description.ByteCodeElement; import net.bytebuddy.description.annotation.AnnotationDescription; import net.bytebuddy.description.annotation.AnnotationList; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.ParameterDescription; import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.modifier.ModifierContributor; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import net.bytebuddy.description.type.TypeVariableToken; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Mock; import java.util.Collections; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.none; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class TransformerForMethodTest { private static final String FOO = "foo", BAR = "bar", QUX = "qux"; private static final int MODIFIERS = 42, RANGE = 3, MASK = 1; @Rule public TestRule mockitoRule = new MockitoRule(this); @Mock private TypeDescription instrumentedType, rawDeclaringType, rawReturnType, rawParameterType; @Mock private Transformer<MethodDescription.Token> tokenTransformer; @Mock private MethodDescription methodDescription; @Mock private MethodDescription.InDefinedShape definedMethod; @Mock private MethodDescription.Token methodToken; @Mock private ParameterDescription.Token parameterToken; @Mock private ParameterDescription.InDefinedShape definedParameter; @Mock private TypeDescription.Generic returnType, typeVariableBound, parameterType, exceptionType, declaringType; @Mock private AnnotationDescription methodAnnotation, parameterAnnotation; @Mock private ModifierContributor.ForMethod modifierContributor; @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { when(returnType.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(returnType); when(typeVariableBound.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(typeVariableBound); when(parameterType.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(parameterType); when(exceptionType.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(exceptionType); when(typeVariableBound.getSymbol()).thenReturn(QUX); when(typeVariableBound.getSort()).thenReturn(TypeDefinition.Sort.VARIABLE); when(typeVariableBound.asGenericType()).thenReturn(typeVariableBound); when(methodDescription.asToken(none())).thenReturn(methodToken); when(methodDescription.getDeclaringType()).thenReturn(declaringType); when(methodDescription.asDefined()).thenReturn(definedMethod); when(methodToken.getName()).thenReturn(FOO); when(methodToken.getModifiers()).thenReturn(MODIFIERS); when(methodToken.getReturnType()).thenReturn(returnType); when(methodToken.getTypeVariableTokens()).thenReturn(new ByteCodeElement.Token.TokenList<TypeVariableToken>(new TypeVariableToken(QUX, new TypeList.Generic.Explicit(typeVariableBound)))); when(methodToken.getExceptionTypes()).thenReturn(new TypeList.Generic.Explicit(exceptionType)); when(methodToken.getParameterTokens()) .thenReturn(new ByteCodeElement.Token.TokenList<ParameterDescription.Token>(parameterToken)); when(methodToken.getAnnotations()).thenReturn(new AnnotationList.Explicit(methodAnnotation)); when(modifierContributor.getMask()).thenReturn(MASK); when(modifierContributor.getRange()).thenReturn(RANGE); when(parameterToken.getType()).thenReturn(parameterType); when(parameterToken.getAnnotations()).thenReturn(new AnnotationList.Explicit(parameterAnnotation)); when(parameterToken.getName()).thenReturn(BAR); when(parameterToken.getModifiers()).thenReturn(MODIFIERS * 2); when(definedMethod.getParameters()) .thenReturn(new ParameterList.Explicit<ParameterDescription.InDefinedShape>(definedParameter)); when(declaringType.asErasure()).thenReturn(rawDeclaringType); when(returnType.asErasure()).thenReturn(rawReturnType); when(parameterType.asErasure()).thenReturn(rawParameterType); when(exceptionType.asGenericType()).thenReturn(exceptionType); } @Test public void testSimpleTransformation() throws Exception { when(tokenTransformer.transform(instrumentedType, methodToken)).thenReturn(methodToken); MethodDescription transformed = new Transformer.ForMethod(tokenTransformer).transform(instrumentedType, methodDescription); assertThat(transformed.getDeclaringType(), is((TypeDefinition) declaringType)); assertThat(transformed.getInternalName(), is(FOO)); assertThat(transformed.getModifiers(), is(MODIFIERS)); assertThat(transformed.getReturnType(), is(returnType)); assertThat(transformed.getTypeVariables().size(), is(1)); assertThat(transformed.getTypeVariables().getOnly().getSymbol(), is(QUX)); assertThat(transformed.getExceptionTypes().size(), is(1)); assertThat(transformed.getExceptionTypes().getOnly(), is(exceptionType)); assertThat(transformed.getDeclaredAnnotations(), is(Collections.singletonList(methodAnnotation))); assertThat(transformed.getParameters().size(), is(1)); assertThat(transformed.getParameters().getOnly().getType(), is(parameterType)); assertThat(transformed.getParameters().getOnly().getName(), is(BAR)); assertThat(transformed.getParameters().getOnly().getModifiers(), is(MODIFIERS * 2)); assertThat(transformed.getParameters().getOnly().getDeclaredAnnotations().size(), is(1)); assertThat(transformed.getParameters().getOnly().getDeclaredAnnotations().getOnly(), is(parameterAnnotation)); assertThat(transformed.getParameters().getOnly().asDefined(), is(definedParameter)); assertThat(transformed.getParameters().getOnly().getDeclaredAnnotations(), is(Collections.singletonList(parameterAnnotation))); assertThat(transformed.getParameters().getOnly().getDeclaringMethod(), is(transformed)); assertThat(transformed.asDefined(), is(definedMethod)); } @Test public void testModifierTransformation() throws Exception { MethodDescription.Token transformed = new Transformer.ForMethod.MethodModifierTransformer(ModifierContributor.Resolver.of(modifierContributor)) .transform(instrumentedType, methodToken); assertThat(transformed.getName(), is(FOO)); assertThat(transformed.getModifiers(), is((MODIFIERS & ~RANGE) | MASK)); assertThat(transformed.getReturnType(), is(returnType)); assertThat(transformed.getTypeVariableTokens().size(), is(1)); assertThat(transformed.getTypeVariableTokens().get(0), is(new TypeVariableToken(QUX, Collections.singletonList(typeVariableBound)))); assertThat(transformed.getExceptionTypes().size(), is(1)); assertThat(transformed.getExceptionTypes().getOnly(), is(exceptionType)); assertThat(transformed.getParameterTokens().size(), is(1)); assertThat(transformed.getParameterTokens().getOnly(), is(parameterToken)); } @Test public void testNoChangesUnlessSpecified() throws Exception { TypeDescription typeDescription = new TypeDescription.ForLoadedType(Bar.class); MethodDescription methodDescription = typeDescription.getSuperClass().getDeclaredMethods().filter(named(FOO)).getOnly(); MethodDescription transformed = Transformer.ForMethod.withModifiers().transform(typeDescription, methodDescription); assertThat(transformed, is(methodDescription)); assertThat(transformed.getModifiers(), is(methodDescription.getModifiers())); } @Test public void testRetainsInstrumentedType() throws Exception { TypeDescription typeDescription = new TypeDescription.ForLoadedType(Bar.class); MethodDescription methodDescription = typeDescription.getSuperClass().getDeclaredMethods().filter(named(BAR)).getOnly(); MethodDescription transformed = Transformer.ForMethod.withModifiers().transform(typeDescription, methodDescription); assertThat(transformed, is(methodDescription)); assertThat(transformed.getModifiers(), is(methodDescription.getModifiers())); assertThat(transformed.getReturnType().asErasure(), is(typeDescription)); assertThat(transformed.getReturnType().getSort(), is(TypeDefinition.Sort.PARAMETERIZED)); assertThat(transformed.getReturnType().getTypeArguments().size(), is(1)); assertThat(transformed.getReturnType().getTypeArguments().getOnly(), is(typeDescription.getSuperClass().getDeclaredMethods().filter(named(FOO)).getOnly().getReturnType())); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(Transformer.ForMethod.class).apply(); ObjectPropertyAssertion.of(Transformer.ForMethod.MethodModifierTransformer.class).apply(); ObjectPropertyAssertion.of(Transformer.ForMethod.TransformedMethod.AttachmentVisitor.class).apply(); } private static class Foo<T> { T foo() { return null; } Bar<T> bar() { return null; } } private static class Bar<S> extends Foo<S> { /* empty */ } }