package net.bytebuddy.implementation.auxiliary; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.field.FieldDescription; import net.bytebuddy.description.field.FieldList; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodList; import net.bytebuddy.description.method.ParameterList; import net.bytebuddy.description.modifier.ModifierContributor; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeList; import net.bytebuddy.dynamic.scaffold.MethodGraph; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.MethodAccessorFactory; import net.bytebuddy.implementation.bytecode.StackManipulation; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.test.utility.MockitoRule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Mock; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.io.Serializable; import java.util.Collections; import static net.bytebuddy.matcher.ElementMatchers.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class TypeProxyCreationTest { private static final String FOO = "foo", BAR = "bar"; @Rule public TestRule mockitoRule = new MockitoRule(this); @Mock private Implementation.Target implementationTarget; @Mock private TypeProxy.InvocationFactory invocationFactory; @Mock private MethodAccessorFactory methodAccessorFactory; @Mock private Implementation.SpecialMethodInvocation specialMethodInvocation; @Mock private MethodDescription.InDefinedShape proxyMethod; private TypeDescription foo; private MethodList<?> fooMethods; private int modifiers; @Before public void setUp() throws Exception { for (ModifierContributor modifierContributor : AuxiliaryType.DEFAULT_TYPE_MODIFIER) { modifiers = modifiers | modifierContributor.getMask(); } foo = new TypeDescription.ForLoadedType(Foo.class); fooMethods = MethodGraph.Compiler.DEFAULT.compile(foo) .listNodes() .asMethodList() .filter(isVirtual().and(not(isFinal())).and(not(isDefaultFinalizer()))); when(proxyMethod.getParameters()).thenReturn(new ParameterList.Explicit.ForTypes(proxyMethod, foo, foo, foo)); when(proxyMethod.getDeclaringType()).thenReturn(foo); when(proxyMethod.getInternalName()).thenReturn(FOO); when(proxyMethod.getDescriptor()).thenReturn(FOO); when(proxyMethod.getReturnType()).thenReturn(TypeDescription.Generic.OBJECT); when(proxyMethod.asDefined()).thenReturn(proxyMethod); } @Test public void testAllIllegal() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); TypeDescription dynamicType = new TypeProxy(foo, implementationTarget, invocationFactory, true, false) .make(BAR, ClassFileVersion.ofThisVm(), methodAccessorFactory) .getTypeDescription(); assertThat(dynamicType.getModifiers(), is(modifiers)); assertThat(dynamicType.getSuperClass().asErasure(), is(foo)); assertThat(dynamicType.getInterfaces(), is((TypeList.Generic) new TypeList.Generic.Empty())); assertThat(dynamicType.getName(), is(BAR)); assertThat(dynamicType.getDeclaredMethods().size(), is(2)); assertThat(dynamicType.isAssignableTo(Serializable.class), is(false)); verifyZeroInteractions(methodAccessorFactory); for (MethodDescription methodDescription : fooMethods) { verify(invocationFactory).invoke(implementationTarget, foo, methodDescription); } verifyNoMoreInteractions(invocationFactory); verify(specialMethodInvocation, times(fooMethods.size())).isValid(); verifyNoMoreInteractions(specialMethodInvocation); } @Test public void testAllLegal() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); TypeDescription dynamicType = new TypeProxy(foo, implementationTarget, invocationFactory, true, false) .make(BAR, ClassFileVersion.ofThisVm(), methodAccessorFactory) .getTypeDescription(); assertThat(dynamicType.getModifiers(), is(modifiers)); assertThat(dynamicType.getSuperClass().asErasure(), is(foo)); assertThat(dynamicType.getInterfaces(), is((TypeList.Generic) new TypeList.Generic.Empty())); assertThat(dynamicType.getName(), is(BAR)); assertThat(dynamicType.getDeclaredMethods().size(), is(2)); assertThat(dynamicType.isAssignableTo(Serializable.class), is(false)); verify(methodAccessorFactory, times(fooMethods.size())).registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT); for (MethodDescription methodDescription : fooMethods) { verify(invocationFactory).invoke(implementationTarget, foo, methodDescription); } verifyNoMoreInteractions(invocationFactory); verify(specialMethodInvocation, times(fooMethods.size())).isValid(); verifyNoMoreInteractions(specialMethodInvocation); } @Test public void testAllLegalSerializable() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); TypeDescription dynamicType = new TypeProxy(foo, implementationTarget, invocationFactory, true, true) .make(BAR, ClassFileVersion.ofThisVm(), methodAccessorFactory) .getTypeDescription(); assertThat(dynamicType.getModifiers(), is(modifiers)); assertThat(dynamicType.getSuperClass().asErasure(), is(foo)); assertThat(dynamicType.getInterfaces(), is((TypeList.Generic) new TypeList.Generic.ForLoadedTypes(Serializable.class))); assertThat(dynamicType.getName(), is(BAR)); assertThat(dynamicType.getDeclaredMethods().size(), is(2)); assertThat(dynamicType.isAssignableTo(Serializable.class), is(true)); verify(methodAccessorFactory, times(fooMethods.size())).registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT); for (MethodDescription methodDescription : fooMethods) { verify(invocationFactory).invoke(implementationTarget, foo, methodDescription); } verifyNoMoreInteractions(invocationFactory); verify(specialMethodInvocation, times(fooMethods.size())).isValid(); verifyNoMoreInteractions(specialMethodInvocation); } @Test public void testAllLegalNotIgnoreFinalizer() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); TypeDescription dynamicType = new TypeProxy(foo, implementationTarget, invocationFactory, false, false) .make(BAR, ClassFileVersion.ofThisVm(), methodAccessorFactory) .getTypeDescription(); assertThat(dynamicType.getModifiers(), is(modifiers)); assertThat(dynamicType.getSuperClass().asErasure(), is(foo)); assertThat(dynamicType.getInterfaces(), is((TypeList.Generic) new TypeList.Generic.Empty())); assertThat(dynamicType.getName(), is(BAR)); assertThat(dynamicType.getDeclaredMethods().size(), is(2)); assertThat(dynamicType.isAssignableTo(Serializable.class), is(false)); verify(methodAccessorFactory, times(fooMethods.size() + 1)).registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT); for (MethodDescription methodDescription : fooMethods) { verify(invocationFactory).invoke(implementationTarget, foo, methodDescription); } verify(invocationFactory).invoke(implementationTarget, foo, new MethodDescription.ForLoadedMethod(Object.class.getDeclaredMethod("finalize"))); verifyNoMoreInteractions(invocationFactory); verify(specialMethodInvocation, times(fooMethods.size() + 1)).isValid(); verifyNoMoreInteractions(specialMethodInvocation); } @Test public void testForConstructorConstruction() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); StackManipulation stackManipulation = new TypeProxy.ForSuperMethodByConstructor(foo, implementationTarget, Collections.singletonList((TypeDescription) new TypeDescription.ForLoadedType(Void.class)), true, false); MethodVisitor methodVisitor = mock(MethodVisitor.class); Implementation.Context implementationContext = mock(Implementation.Context.class); when(implementationContext.register(any(AuxiliaryType.class))).thenReturn(foo); assertThat(stackManipulation.isValid(), is(true)); StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); assertThat(size.getSizeImpact(), is(1)); assertThat(size.getMaximalSize(), is(3)); verify(implementationContext).register(any(AuxiliaryType.class)); verifyNoMoreInteractions(implementationContext); verify(methodVisitor).visitTypeInsn(Opcodes.NEW, Type.getInternalName(Foo.class)); verify(methodVisitor, times(2)).visitInsn(Opcodes.DUP); verify(methodVisitor).visitInsn(Opcodes.ACONST_NULL); verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESPECIAL, foo.getInternalName(), MethodDescription.CONSTRUCTOR_INTERNAL_NAME, foo.getDeclaredMethods().filter(isConstructor()).getOnly().getDescriptor(), false); verify(methodVisitor).visitFieldInsn(Opcodes.PUTFIELD, foo.getInternalName(), TypeProxy.INSTANCE_FIELD, Type.getDescriptor(Void.class)); verify(methodVisitor).visitVarInsn(Opcodes.ALOAD, 0); verifyNoMoreInteractions(methodVisitor); } @Test public void testForDefaultMethodConstruction() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); StackManipulation stackManipulation = new TypeProxy.ForDefaultMethod(foo, implementationTarget, false); MethodVisitor methodVisitor = mock(MethodVisitor.class); Implementation.Context implementationContext = mock(Implementation.Context.class); when(implementationContext.register(any(AuxiliaryType.class))).thenReturn(foo); assertThat(stackManipulation.isValid(), is(true)); StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); assertThat(size.getSizeImpact(), is(0)); assertThat(size.getMaximalSize(), is(2)); verify(implementationContext).register(any(AuxiliaryType.class)); verifyNoMoreInteractions(implementationContext); verify(methodVisitor).visitTypeInsn(Opcodes.NEW, Type.getInternalName(Foo.class)); verify(methodVisitor, times(2)).visitInsn(Opcodes.DUP); verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESPECIAL, foo.getInternalName(), MethodDescription.CONSTRUCTOR_INTERNAL_NAME, foo.getDeclaredMethods().filter(isConstructor()).getOnly().getDescriptor(), false); verify(methodVisitor).visitFieldInsn(Opcodes.PUTFIELD, foo.getInternalName(), TypeProxy.INSTANCE_FIELD, Type.getDescriptor(Void.class)); verify(methodVisitor).visitVarInsn(Opcodes.ALOAD, 0); verifyNoMoreInteractions(methodVisitor); } @Test public void testForReflectionFactoryConstruction() throws Exception { when(implementationTarget.getInstrumentedType()).thenReturn(foo); when(invocationFactory.invoke(eq(implementationTarget), eq(foo), any(MethodDescription.class))) .thenReturn(specialMethodInvocation); when(specialMethodInvocation.isValid()).thenReturn(true); when(specialMethodInvocation.apply(any(MethodVisitor.class), any(Implementation.Context.class))) .thenReturn(new StackManipulation.Size(0, 0)); when(methodAccessorFactory.registerAccessorFor(specialMethodInvocation, MethodAccessorFactory.AccessType.DEFAULT)).thenReturn(proxyMethod); StackManipulation stackManipulation = new TypeProxy.ForSuperMethodByReflectionFactory(foo, implementationTarget, true, false); MethodVisitor methodVisitor = mock(MethodVisitor.class); Implementation.Context implementationContext = mock(Implementation.Context.class); when(implementationContext.register(any(AuxiliaryType.class))) .thenReturn(new TypeDescription.ForLoadedType(FooProxyMake.class)); assertThat(stackManipulation.isValid(), is(true)); StackManipulation.Size size = stackManipulation.apply(methodVisitor, implementationContext); assertThat(size.getSizeImpact(), is(1)); assertThat(size.getMaximalSize(), is(3)); verify(implementationContext).register(any(AuxiliaryType.class)); verifyNoMoreInteractions(implementationContext); verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(FooProxyMake.class), TypeProxy.REFLECTION_METHOD, Type.getMethodDescriptor(FooProxyMake.class.getDeclaredMethod("make")), false); verify(methodVisitor).visitInsn(Opcodes.DUP); verify(methodVisitor).visitFieldInsn(Opcodes.PUTFIELD, Type.getInternalName(FooProxyMake.class), TypeProxy.INSTANCE_FIELD, Type.getDescriptor(Void.class)); verify(methodVisitor).visitVarInsn(Opcodes.ALOAD, 0); verifyNoMoreInteractions(methodVisitor); } @Test public void testImplementationIsValid() throws Exception { assertThat(TypeProxy.AbstractMethodErrorThrow.INSTANCE.isValid(), is(true)); } @Test @SuppressWarnings("unchecked") public void testAccessorIsValid() throws Exception { TypeProxy typeProxy = new TypeProxy(mock(TypeDescription.class), mock(Implementation.Target.class), mock(TypeProxy.InvocationFactory.class), false, false); TypeProxy.MethodCall methodCall = typeProxy.new MethodCall(mock(MethodAccessorFactory.class)); TypeDescription instrumentedType = mock(TypeDescription.class); FieldList<FieldDescription.InDefinedShape> fieldList = mock(FieldList.class); when(fieldList.filter(any(ElementMatcher.class))).thenReturn(fieldList); when(fieldList.getOnly()).thenReturn(mock(FieldDescription.InDefinedShape.class)); when(instrumentedType.getDeclaredFields()).thenReturn(fieldList); TypeProxy.MethodCall.Appender appender = methodCall.new Appender(instrumentedType); Implementation.SpecialMethodInvocation specialMethodInvocation = mock(Implementation.SpecialMethodInvocation.class); when(specialMethodInvocation.isValid()).thenReturn(true); StackManipulation stackManipulation = appender.new AccessorMethodInvocation(mock(MethodDescription.class), specialMethodInvocation); assertThat(stackManipulation.isValid(), is(true)); verify(specialMethodInvocation).isValid(); verifyNoMoreInteractions(specialMethodInvocation); } @SuppressWarnings("unused") public static class Foo { private Void target; public Foo(Void argument) { } } @SuppressWarnings("unused") public static class FooProxyMake { private Void target; public static FooProxyMake make() { return null; } } }