package net.bytebuddy.implementation.bind; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.implementation.bytecode.StackSize; import net.bytebuddy.test.utility.MockitoRule; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Answers; import org.mockito.Mock; import org.objectweb.asm.MethodVisitor; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class MethodBindingBuilderTest { private static final String FOO = "foo"; private static final String BAR = "bar"; private static final String BAZ = "baz"; @Rule public TestRule mockitoRule = new MockitoRule(this); @Mock private MethodDescription methodDescription; @Mock private ParameterList<?> methodParameterList; @Mock private TargetMethodAnnotationDrivenBinder.MethodInvoker methodInvoker; @Mock private MethodVisitor methodVisitor; @Mock private TypeDescription.Generic returnType; @Mock(answer = Answers.RETURNS_MOCKS) private StackManipulation legalStackManipulation, illegalStackManipulation; @Mock private Implementation.Context implementationContext; @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { when(methodDescription.getParameters()).thenReturn((ParameterList) methodParameterList); when(methodDescription.isStatic()).thenReturn(false); TypeDescription declaringType = mock(TypeDescription.class); when(methodDescription.getDeclaringType()).thenReturn(declaringType); when(declaringType.getInternalName()).thenReturn(FOO); when(declaringType.isInterface()).thenReturn(false); when(methodDescription.getInternalName()).thenReturn(BAR); when(methodDescription.getDescriptor()).thenReturn(BAZ); when(methodDescription.getStackSize()).thenReturn(0); when(methodDescription.getReturnType()).thenReturn(returnType); when(returnType.getStackSize()).thenReturn(StackSize.ZERO); when(legalStackManipulation.isValid()).thenReturn(true); when(illegalStackManipulation.isValid()).thenReturn(false); } @After public void tearDown() throws Exception { verifyZeroInteractions(implementationContext); } @Test public void testIllegalReturnTypeBinding() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); MethodDelegationBinder.MethodBinding methodBinding = builder.build(illegalStackManipulation); assertThat(methodBinding.isValid(), is(false)); assertThat(methodBinding.getTarget(), is(methodDescription)); } @Test public void testLegalReturnTypeBinding() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); MethodDelegationBinder.MethodBinding methodBinding = builder.build(legalStackManipulation); assertThat(methodBinding.isValid(), is(true)); assertThat(methodBinding.getTarget(), is(methodDescription)); methodBinding.apply(methodVisitor, implementationContext); verify(legalStackManipulation, times(2)).apply(methodVisitor, implementationContext); verifyZeroInteractions(methodVisitor); } @Test public void testIllegalParameterTypeBinding() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); when(methodParameterList.size()).thenReturn(2); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Object())), is(true)); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(illegalStackManipulation, new Object())), is(true)); MethodDelegationBinder.MethodBinding methodBinding = builder.build(legalStackManipulation); assertThat(methodBinding.isValid(), is(false)); assertThat(methodBinding.getTarget(), is(methodDescription)); } @Test public void testLegalParameterTypeBinding() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); when(methodParameterList.size()).thenReturn(2); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Object())), is(true)); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Object())), is(true)); MethodDelegationBinder.MethodBinding methodBinding = builder.build(legalStackManipulation); assertThat(methodBinding.isValid(), is(true)); assertThat(methodBinding.getTarget(), is(methodDescription)); methodBinding.apply(methodVisitor, implementationContext); verify(legalStackManipulation, times(4)).apply(methodVisitor, implementationContext); verifyZeroInteractions(methodVisitor); } @Test public void testUniqueIdentification() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); when(methodParameterList.size()).thenReturn(2); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Key(FOO))), is(true)); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Key(BAR))), is(true)); MethodDelegationBinder.MethodBinding methodBinding = builder.build(legalStackManipulation); assertThat(methodBinding.getTargetParameterIndex(new Key(FOO)), is(0)); assertThat(methodBinding.getTargetParameterIndex(new Key(BAR)), is(1)); assertThat(methodBinding.isValid(), is(true)); assertThat(methodBinding.getTarget(), is(methodDescription)); } @Test public void testNonUniqueIdentification() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Key(FOO))), is(true)); assertThat(builder.append(MethodDelegationBinder.ParameterBinding.Unique.of(legalStackManipulation, new Key(FOO))), is(false)); } @Test(expected = IllegalStateException.class) public void testParameterNumberInequality() throws Exception { when(methodParameterList.size()).thenReturn(1); new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription).build(legalStackManipulation); } @Test public void testBuildHashCodeEquals() throws Exception { when(methodInvoker.invoke(any(MethodDescription.class))).thenReturn(legalStackManipulation); MethodDelegationBinder.MethodBinding.Builder builder = new MethodDelegationBinder.MethodBinding.Builder(methodInvoker, methodDescription); MethodDelegationBinder.MethodBinding methodBinding = builder.build(legalStackManipulation); MethodDelegationBinder.MethodBinding equalMethodBinding = builder.build(legalStackManipulation); assertThat(methodBinding.hashCode(), is(equalMethodBinding.hashCode())); assertThat(methodBinding, is(equalMethodBinding)); MethodDelegationBinder.MethodBinding unequalMethodBinding = builder.build(mock(StackManipulation.class)); assertThat(methodBinding.hashCode(), not(unequalMethodBinding.hashCode())); assertThat(methodBinding, not(unequalMethodBinding)); } private static class Key { private final String identifier; private Key(String identifier) { this.identifier = identifier; } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && identifier.equals(((Key) other).identifier); } @Override public int hashCode() { return identifier.hashCode(); } } }