package net.bytebuddy.dynamic.scaffold; import net.bytebuddy.ClassFileVersion; import net.bytebuddy.description.annotation.AnnotationList; 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.modifier.Visibility; import net.bytebuddy.description.type.TypeDefinition; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.Transformer; import net.bytebuddy.implementation.Implementation; import net.bytebuddy.implementation.LoadedTypeInitializer; import net.bytebuddy.implementation.attribute.MethodAttributeAppender; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.matcher.LatentMatcher; 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 org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class MethodRegistryDefaultTest { @Rule public TestRule mockitoRule = new MockitoRule(this); @Mock private ClassFileVersion classFileVersion; @Mock private LatentMatcher<MethodDescription> firstMatcher, secondMatcher, methodFilter; @Mock private MethodRegistry.Handler firstHandler, secondHandler; @Mock private MethodRegistry.Handler.Compiled firstCompiledHandler, secondCompiledHandler; @Mock private TypeWriter.MethodPool.Record firstRecord, secondRecord; @Mock private MethodAttributeAppender.Factory firstFactory, secondFactory; @Mock private MethodAttributeAppender firstAppender, secondAppender; @Mock private InstrumentedType firstType, secondType, thirdType; @Mock private TypeDescription typeDescription; @Mock private MethodDescription instrumentedMethod; @Mock private MethodGraph.Compiler methodGraphCompiler; @Mock private MethodGraph.Linked methodGraph; @Mock private TypeInitializer typeInitializer; @Mock private LoadedTypeInitializer loadedTypeInitializer; @Mock private ElementMatcher<? super MethodDescription> resolvedMethodFilter, firstFilter, secondFilter; @Mock private Implementation.Target.Factory implementationTargetFactory; @Mock private Implementation.Target implementationTarget; @Mock private Transformer<MethodDescription> transformer; @Mock private TypeDescription returnType, parameterType; @Mock private TypeDescription.Generic genericReturnType, genericParameterType; @Mock private ParameterDescription.InDefinedShape parameterDescription; @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { when(firstHandler.prepare(firstType)).thenReturn(secondType); when(secondHandler.prepare(secondType)).thenReturn(thirdType); when(firstHandler.compile(implementationTarget)).thenReturn(firstCompiledHandler); when(secondHandler.compile(implementationTarget)).thenReturn(secondCompiledHandler); when(thirdType.getTypeInitializer()).thenReturn(typeInitializer); when(thirdType.getLoadedTypeInitializer()).thenReturn(loadedTypeInitializer); when(methodGraphCompiler.compile(thirdType)).thenReturn(methodGraph); when(methodGraph.listNodes()).thenReturn(new MethodGraph.NodeList(Collections.singletonList(new MethodGraph.Node.Simple(instrumentedMethod)))); when(firstType.getDeclaredMethods()).thenReturn(new MethodList.Empty<MethodDescription.InDefinedShape>()); when(secondType.getDeclaredMethods()).thenReturn(new MethodList.Empty<MethodDescription.InDefinedShape>()); when(thirdType.getDeclaredMethods()).thenReturn(new MethodList.Empty<MethodDescription.InDefinedShape>()); when(methodFilter.resolve(thirdType)).thenReturn((ElementMatcher) resolvedMethodFilter); when(firstMatcher.resolve(thirdType)).thenReturn((ElementMatcher) firstFilter); when(secondMatcher.resolve(thirdType)).thenReturn((ElementMatcher) secondFilter); when(firstFactory.make(typeDescription)).thenReturn(firstAppender); when(secondFactory.make(typeDescription)).thenReturn(secondAppender); when(implementationTargetFactory.make(typeDescription, methodGraph, classFileVersion)).thenReturn(implementationTarget); when(firstCompiledHandler.assemble(instrumentedMethod, firstAppender, Visibility.PUBLIC)).thenReturn(firstRecord); when(secondCompiledHandler.assemble(instrumentedMethod, secondAppender, Visibility.PUBLIC)).thenReturn(secondRecord); when(transformer.transform(thirdType, instrumentedMethod)).thenReturn(instrumentedMethod); when(thirdType.validated()).thenReturn(typeDescription); when(implementationTarget.getInstrumentedType()).thenReturn(typeDescription); when(genericReturnType.asErasure()).thenReturn(returnType); when(genericReturnType.getSort()).thenReturn(TypeDefinition.Sort.NON_GENERIC); when(returnType.isVisibleTo(thirdType)).thenReturn(true); when(genericParameterType.asErasure()).thenReturn(parameterType); when(genericParameterType.getSort()).thenReturn(TypeDefinition.Sort.NON_GENERIC); when(parameterType.isVisibleTo(thirdType)).thenReturn(true); when(instrumentedMethod.getReturnType()).thenReturn(genericReturnType); when(instrumentedMethod.getParameters()).thenReturn((ParameterList) new ParameterList.Explicit<ParameterDescription>(parameterDescription)); when(parameterDescription.getType()).thenReturn(genericParameterType); when(instrumentedMethod.getVisibility()).thenReturn(Visibility.PUBLIC); } @Test public void testNonMatchedIsNotIncluded() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Prepared methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods().size(), is(0)); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); } @Test public void testIgnoredIsNotIncluded() throws Exception { when(firstFilter.matches(instrumentedMethod)).thenReturn(true); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Prepared methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods().size(), is(0)); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); } @Test @SuppressWarnings("unchecked") public void testMatchedFirst() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Prepared methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); } @Test @SuppressWarnings("unchecked") public void testMatchedSecond() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Prepared methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); } @Test @SuppressWarnings("unchecked") public void testMultipleRegistryDoesNotPrepareMultipleTimes() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(true); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Prepared methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, firstHandler, secondFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .append(firstMatcher, secondHandler, secondFactory, transformer) .append(firstMatcher, firstHandler, secondFactory, transformer) .append(firstMatcher, secondHandler, firstFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); } @Test @SuppressWarnings("unchecked") public void testCompiledAppendingMatchesFirstAppended() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(true); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Compiled methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter) .compile(implementationTargetFactory, classFileVersion); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); verify(firstFactory).make(typeDescription); verifyZeroInteractions(secondFactory); assertThat(methodRegistry.target(instrumentedMethod), is(firstRecord)); } @Test @SuppressWarnings("unchecked") public void testCompiledPrependingMatchesLastPrepended() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(true); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Compiled methodRegistry = new MethodRegistry.Default() .append(secondMatcher, secondHandler, secondFactory, transformer) .prepend(firstMatcher, firstHandler, firstFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter) .compile(implementationTargetFactory, classFileVersion); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); verify(firstFactory).make(typeDescription); verifyZeroInteractions(secondFactory); assertThat(methodRegistry.target(instrumentedMethod), is(firstRecord)); } @Test @SuppressWarnings("unchecked") public void testCompiledAppendingMatchesSecondAppendedIfFirstDoesNotMatch() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(false); when(secondFilter.matches(instrumentedMethod)).thenReturn(true); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); MethodRegistry.Compiled methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter) .compile(implementationTargetFactory, classFileVersion); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods(), is((MethodList) new MethodList.Explicit(instrumentedMethod))); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); verifyZeroInteractions(firstFactory); verify(secondFactory).make(typeDescription); assertThat(methodRegistry.target(instrumentedMethod), is(secondRecord)); } @Test public void testSkipEntryIfNotMatchedAndVisible() throws Exception { when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(false); when(secondFilter.matches(instrumentedMethod)).thenReturn(false); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); TypeDescription declaringType = mock(TypeDescription.class); when(declaringType.asErasure()).thenReturn(declaringType); when(instrumentedMethod.getDeclaringType()).thenReturn(declaringType); MethodRegistry.Compiled methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter) .compile(implementationTargetFactory, classFileVersion); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods().size(), is(0)); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); verifyZeroInteractions(firstFactory); verifyZeroInteractions(secondFactory); assertThat(methodRegistry.target(instrumentedMethod), instanceOf(TypeWriter.MethodPool.Record.ForNonImplementedMethod.class)); } @Test @SuppressWarnings("unchecked") public void testVisibilityBridgeIfNotMatchedAndVisible() throws Exception { when(instrumentedMethod.getDeclaredAnnotations()).thenReturn(new AnnotationList.Empty()); when(parameterDescription.getDeclaredAnnotations()).thenReturn(new AnnotationList.Empty()); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); when(firstFilter.matches(instrumentedMethod)).thenReturn(false); when(secondFilter.matches(instrumentedMethod)).thenReturn(false); when(resolvedMethodFilter.matches(instrumentedMethod)).thenReturn(true); TypeDescription declaringType = mock(TypeDescription.class); when(declaringType.asErasure()).thenReturn(declaringType); when(instrumentedMethod.getDeclaringType()).thenReturn(declaringType); when(thirdType.isPublic()).thenReturn(true); when(instrumentedMethod.isPublic()).thenReturn(true); when(declaringType.isPackagePrivate()).thenReturn(true); TypeDescription.Generic superClass = mock(TypeDescription.Generic.class); TypeDescription rawSuperClass = mock(TypeDescription.class); when(superClass.asErasure()).thenReturn(rawSuperClass); when(typeDescription.getSuperClass()).thenReturn(superClass); MethodDescription.Token methodToken = mock(MethodDescription.Token.class); when(instrumentedMethod.asToken(ElementMatchers.is(typeDescription))).thenReturn(methodToken); when(methodToken.accept(any(TypeDescription.Generic.Visitor.class))).thenReturn(methodToken); when(classFileVersion.isAtLeast(ClassFileVersion.JAVA_V5)).thenReturn(true); MethodRegistry.Compiled methodRegistry = new MethodRegistry.Default() .append(firstMatcher, firstHandler, firstFactory, transformer) .append(secondMatcher, secondHandler, secondFactory, transformer) .prepare(firstType, methodGraphCompiler, TypeValidation.ENABLED, methodFilter) .compile(implementationTargetFactory, classFileVersion); assertThat(methodRegistry.getInstrumentedType(), is(typeDescription)); assertThat(methodRegistry.getInstrumentedMethods().size(), is(1)); assertThat(methodRegistry.getTypeInitializer(), is(typeInitializer)); assertThat(methodRegistry.getLoadedTypeInitializer(), is(loadedTypeInitializer)); verify(firstHandler).prepare(firstType); verify(secondHandler).prepare(secondType); verifyZeroInteractions(firstFactory); verifyZeroInteractions(secondFactory); assertThat(methodRegistry.target(instrumentedMethod), instanceOf(TypeWriter.MethodPool.Record.ForDefinedMethod.OfVisibilityBridge.class)); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(MethodRegistry.Default.class).apply(); ObjectPropertyAssertion.of(MethodRegistry.Default.Entry.class).apply(); ObjectPropertyAssertion.of(MethodRegistry.Default.Prepared.class).apply(); ObjectPropertyAssertion.of(MethodRegistry.Default.Prepared.Entry.class).apply(); ObjectPropertyAssertion.of(MethodRegistry.Default.Compiled.class).apply(); ObjectPropertyAssertion.of(MethodRegistry.Default.Compiled.Entry.class).apply(); } }