package net.bytebuddy.dynamic.scaffold.subclass;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.method.ParameterDescription;
import net.bytebuddy.description.method.ParameterList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.scaffold.MethodGraph;
import net.bytebuddy.implementation.AbstractImplementationTargetTest;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.implementation.bytecode.StackManipulation;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.*;
public class SubclassImplementationTargetTest extends AbstractImplementationTargetTest {
private static final String BAR = "bar", BAZ = "baz";
@Mock
private TypeDescription.Generic superClass;
@Mock
private TypeDescription rawSuperClass;
@Mock
private MethodDescription.InGenericShape superClassConstructor;
@Mock
private MethodDescription.InDefinedShape definedSuperClassConstructor;
@Mock
private MethodDescription.SignatureToken superConstructorToken;
@Override
@Before
public void setUp() throws Exception {
when(superGraph.locate(Mockito.any(MethodDescription.SignatureToken.class))).thenReturn(MethodGraph.Node.Unresolved.INSTANCE);
when(superGraph.locate(invokableToken)).thenReturn(new MethodGraph.Node.Simple(invokableMethod));
when(instrumentedType.getSuperClass()).thenReturn(superClass);
when(superClass.asErasure()).thenReturn(rawSuperClass);
when(superClass.asGenericType()).thenReturn(superClass);
when(rawSuperClass.asGenericType()).thenReturn(superClass);
when(rawSuperClass.asErasure()).thenReturn(rawSuperClass);
when(rawSuperClass.getInternalName()).thenReturn(BAR);
when(superClass.getDeclaredMethods())
.thenReturn(new MethodList.Explicit<MethodDescription.InGenericShape>(superClassConstructor));
when(superClassConstructor.asDefined()).thenReturn(definedSuperClassConstructor);
when(definedSuperClassConstructor.getReturnType()).thenReturn(TypeDescription.Generic.VOID);
when(definedSuperClassConstructor.getDeclaringType()).thenReturn(rawSuperClass);
when(definedSuperClassConstructor.isConstructor()).thenReturn(true);
when(superClassConstructor.isVisibleTo(instrumentedType)).thenReturn(true);
when(superClassConstructor.asSignatureToken()).thenReturn(superConstructorToken);
when(definedSuperClassConstructor.getInternalName()).thenReturn(QUX);
when(definedSuperClassConstructor.getDescriptor()).thenReturn(BAZ);
when(superClassConstructor.isConstructor()).thenReturn(true);
when(superClassConstructor.getDeclaringType()).thenReturn(superClass);
when(superClassConstructor.getReturnType()).thenReturn(TypeDescription.Generic.VOID);
when(superClassConstructor.getParameters()).thenReturn(new ParameterList.Empty<ParameterDescription.InGenericShape>());
when(invokableToken.getName()).thenReturn(FOO);
when(superConstructorToken.getName()).thenReturn(MethodDescription.CONSTRUCTOR_INTERNAL_NAME);
super.setUp();
}
@Override
protected Implementation.Target makeImplementationTarget() {
return new SubclassImplementationTarget(instrumentedType, methodGraph, defaultMethodInvocation, SubclassImplementationTarget.OriginTypeResolver.SUPER_CLASS);
}
@Test
public void testSuperTypeMethodIsInvokable() throws Exception {
when(invokableMethod.isSpecializableFor(rawSuperClass)).thenReturn(true);
Implementation.SpecialMethodInvocation specialMethodInvocation = makeImplementationTarget().invokeSuper(invokableToken);
assertThat(specialMethodInvocation.isValid(), is(true));
assertThat(specialMethodInvocation.getMethodDescription(), is((MethodDescription) invokableMethod));
assertThat(specialMethodInvocation.getTypeDescription(), is(rawSuperClass));
MethodVisitor methodVisitor = mock(MethodVisitor.class);
Implementation.Context implementationContext = mock(Implementation.Context.class);
StackManipulation.Size size = specialMethodInvocation.apply(methodVisitor, implementationContext);
verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESPECIAL, BAR, FOO, QUX, false);
verifyNoMoreInteractions(methodVisitor);
verifyZeroInteractions(implementationContext);
assertThat(size.getSizeImpact(), is(0));
assertThat(size.getMaximalSize(), is(0));
}
@Test
public void testNonSpecializableSuperClassMethodIsNotInvokable() throws Exception {
when(invokableMethod.isSpecializableFor(rawSuperClass)).thenReturn(false);
Implementation.SpecialMethodInvocation specialMethodInvocation = makeImplementationTarget().invokeSuper(invokableToken);
assertThat(specialMethodInvocation.isValid(), is(false));
}
@Test
public void testSuperConstructorIsInvokable() throws Exception {
when(invokableMethod.isConstructor()).thenReturn(true);
when(definedSuperClassConstructor.isSpecializableFor(rawSuperClass)).thenReturn(true);
Implementation.SpecialMethodInvocation specialMethodInvocation = makeImplementationTarget().invokeSuper(superConstructorToken);
assertThat(specialMethodInvocation.isValid(), is(true));
assertThat(specialMethodInvocation.getMethodDescription(), is((MethodDescription) superClassConstructor));
assertThat(specialMethodInvocation.getTypeDescription(), is(rawSuperClass));
MethodVisitor methodVisitor = mock(MethodVisitor.class);
Implementation.Context implementationContext = mock(Implementation.Context.class);
StackManipulation.Size size = specialMethodInvocation.apply(methodVisitor, implementationContext);
verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESPECIAL, BAR, QUX, BAZ, false);
verifyNoMoreInteractions(methodVisitor);
verifyZeroInteractions(implementationContext);
assertThat(size.getSizeImpact(), is(0));
assertThat(size.getMaximalSize(), is(0));
}
@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(SubclassImplementationTarget.class).apply();
ObjectPropertyAssertion.of(SubclassImplementationTarget.OriginTypeResolver.class).apply();
}
}