package net.bytebuddy.agent.builder; import net.bytebuddy.agent.ByteBuddyAgent; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.ClassFileLocator; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.TargetType; import net.bytebuddy.dynamic.loading.ByteArrayClassLoader; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy; import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.bind.annotation.Super; import net.bytebuddy.implementation.bind.annotation.SuperCall; import net.bytebuddy.implementation.bytecode.Removal; import net.bytebuddy.implementation.bytecode.assign.Assigner; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.test.packaging.SimpleType; import net.bytebuddy.test.utility.AgentAttachmentRule; import net.bytebuddy.test.utility.ClassFileExtraction; import net.bytebuddy.test.utility.IntegrationRule; import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.utility.JavaModule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.Instrumentation; import java.lang.reflect.Method; import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collection; import java.util.concurrent.Callable; import java.util.logging.Logger; import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.*; import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; @RunWith(Parameterized.class) public class AgentBuilderDefaultApplicationTest { private static final String FOO = "foo", BAR = "bar", QUX = "qux"; private static final String LAMBDA_SAMPLE_FACTORY = "net.bytebuddy.test.precompiled.LambdaSampleFactory"; @Parameterized.Parameters public static Collection<Object[]> data() { // Travis runs out of memory if all of these tests are run. This property serves as a protection (on some profiles). if (Boolean.getBoolean("net.bytebuddy.test.travis")) { Logger.getLogger("net.bytebuddy").info("Running only a subset of type locator tests on Travis CI."); return Arrays.asList(new Object[][]{ {AgentBuilder.PoolStrategy.Default.EXTENDED}, {AgentBuilder.PoolStrategy.Eager.EXTENDED}, {AgentBuilder.PoolStrategy.ClassLoading.EXTENDED} }); } return Arrays.asList(new Object[][]{ {AgentBuilder.PoolStrategy.Default.EXTENDED}, {AgentBuilder.PoolStrategy.Default.FAST}, {AgentBuilder.PoolStrategy.Eager.EXTENDED}, {AgentBuilder.PoolStrategy.Eager.FAST}, {AgentBuilder.PoolStrategy.ClassLoading.EXTENDED}, {AgentBuilder.PoolStrategy.ClassLoading.FAST} }); } @Rule public MethodRule agentAttachmentRule = new AgentAttachmentRule(); @Rule public MethodRule javaVersionRule = new JavaVersionRule(); @Rule public MethodRule integrationRule = new IntegrationRule(); private ClassLoader classLoader; private final AgentBuilder.PoolStrategy poolStrategy; public AgentBuilderDefaultApplicationTest(AgentBuilder.PoolStrategy poolStrategy) { this.poolStrategy = poolStrategy; } @Before public void setUp() throws Exception { classLoader = new ByteArrayClassLoader.ChildFirst(getClass().getClassLoader(), ClassFileExtraction.of(Foo.class, Bar.class, Qux.class, Baz.class, QuxBaz.class, SimpleType.class), ByteArrayClassLoader.PersistenceHandler.MANIFEST); } private ClassLoader lambdaSamples() throws Exception { return new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassFileExtraction.of(Class.forName(LAMBDA_SAMPLE_FACTORY)), ByteArrayClassLoader.PersistenceHandler.MANIFEST); } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testAgentWithoutSelfInitialization() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testAgentSelfInitialization() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Bar.class), ElementMatchers.is(classLoader)).transform(new BarTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Bar.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testAgentSelfInitializationAuxiliaryTypeEager() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Qux.class), ElementMatchers.is(classLoader)).transform(new QuxTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Qux.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testAgentSelfInitializationAuxiliaryTypeLazy() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(QuxBaz.class), ElementMatchers.is(classLoader)).transform(new QuxBazTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(QuxBaz.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testAgentWithoutSelfInitializationWithNativeMethodPrefix() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .enableNativeMethodPrefix(QUX) .type(ElementMatchers.is(Baz.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Baz.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); assertThat(type.getDeclaredMethod(QUX + FOO), notNullValue(Method.class)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testRedefinition() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testRedefinitionWithReset() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { assertThat(classFileTransformer.reset(ByteBuddyAgent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.REDEFINITION), is(true)); } Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) FOO)); } @Test @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testEmptyRedefinition() throws Exception { ByteBuddyAgent.getInstrumentation().removeTransformer(new AgentBuilder.Default() .with(poolStrategy) .ignore(any()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .installOnByteBuddyAgent()); } @Test @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testChunkedRedefinition() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(1)) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testEmptyChunkedRedefinition() throws Exception { ByteBuddyAgent.getInstrumentation().removeTransformer(new AgentBuilder.Default() .with(poolStrategy) .ignore(any()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) .with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(1)) .installOnByteBuddyAgent()); } @Test @AgentAttachmentRule.Enforce(retransformsClasses = true) @IntegrationRule.Enforce public void testRetransformation() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce(retransformsClasses = true) @IntegrationRule.Enforce public void testRetransformationWithReset() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ResettableClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { assertThat(classFileTransformer.reset(ByteBuddyAgent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION), is(true)); } Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) FOO)); } @Test @AgentAttachmentRule.Enforce(retransformsClasses = true) @IntegrationRule.Enforce public void testEmptyRetransformation() throws Exception { ByteBuddyAgent.getInstrumentation().removeTransformer(new AgentBuilder.Default() .with(poolStrategy) .ignore(any()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .installOnByteBuddyAgent()); } @Test @AgentAttachmentRule.Enforce(retransformsClasses = true) @IntegrationRule.Enforce public void testChunkedRetransformation() throws Exception { // A redefinition reflects on loaded types which are eagerly validated types (Java 7- for redefinition). // This causes type equality for outer/inner classes to fail which is why an external class is used. assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); assertThat(classLoader.loadClass(SimpleType.class.getName()).getName(), is(SimpleType.class.getName())); // ensure that class is loaded ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(1)) .type(ElementMatchers.is(SimpleType.class), ElementMatchers.is(classLoader)).transform(new FooTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(SimpleType.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce(retransformsClasses = true) @IntegrationRule.Enforce public void testChunkedEmptyRetransformation() throws Exception { ByteBuddyAgent.getInstrumentation().removeTransformer(new AgentBuilder.Default() .with(poolStrategy) .ignore(any()) .disableClassFormatChanges() .with(AgentBuilder.TypeStrategy.Default.REDEFINE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(1)) .installOnByteBuddyAgent()); } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testChainedAgent() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); AgentBuilder agentBuilder = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Qux.class), ElementMatchers.is(classLoader)).transform(new QuxTransformer()); ClassFileTransformer firstTransformer = agentBuilder.installOnByteBuddyAgent(); ClassFileTransformer secondTransformer = agentBuilder.installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Qux.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR + BAR))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(firstTransformer); ByteBuddyAgent.getInstrumentation().removeTransformer(secondTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testSignatureTypesAreAvailableAfterLoad() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new ConstructorTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredConstructors().length, is(2)); assertThat(type.getDeclaredConstructor().newInstance(), notNullValue(Object.class)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testDecoration() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new BarAdviceTransformer()) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new QuxAdviceTransformer()).asDecorator() .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR + QUX))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testDecorationFallThrough() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new BarAdviceTransformer()).asDecorator() .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new QuxAdviceTransformer()).asDecorator() .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR + QUX))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @AgentAttachmentRule.Enforce @IntegrationRule.Enforce public void testDecorationBlocked() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new BarAdviceTransformer()).asDecorator() .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new QuxAdviceTransformer()) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + QUX))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testNonCapturingLambda() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); @SuppressWarnings("unchecked") Callable<String> instance = (Callable<String>) sampleFactory.getDeclaredMethod("nonCapturing").invoke(sampleFactory.getDeclaredConstructor().newInstance()); assertThat(instance.call(), is(BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testNonCapturingLambdaIsConstant() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); assertThat(sampleFactory.getDeclaredMethod("nonCapturing").invoke(sampleFactory.getDeclaredConstructor().newInstance()), sameInstance(sampleFactory.getDeclaredMethod("nonCapturing").invoke(sampleFactory.getDeclaredConstructor().newInstance()))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testLambdaFactoryIsReset() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .installOn(ByteBuddyAgent.getInstrumentation()); ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); @SuppressWarnings("unchecked") Callable<String> instance = (Callable<String>) sampleFactory.getDeclaredMethod("nonCapturing").invoke(sampleFactory.getDeclaredConstructor().newInstance()); assertThat(instance.call(), is(FOO)); } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testArgumentCapturingLambda() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); @SuppressWarnings("unchecked") Callable<String> instance = (Callable<String>) sampleFactory.getDeclaredMethod("argumentCapturing", String.class).invoke(sampleFactory.getDeclaredConstructor().newInstance(), FOO); assertThat(instance.call(), is(BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testArgumentCapturingLambdaIsNotConstant() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); assertThat(sampleFactory.getDeclaredMethod("argumentCapturing", String.class).invoke(sampleFactory.getDeclaredConstructor().newInstance(), FOO), not(sameInstance(sampleFactory.getDeclaredMethod("argumentCapturing", String.class).invoke(sampleFactory.getDeclaredConstructor().newInstance(), FOO)))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testInstanceCapturingLambda() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Callable.class)).transform(new SingleMethodReplacer("call")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); @SuppressWarnings("unchecked") Callable<String> instance = (Callable<String>) sampleFactory.getDeclaredMethod("instanceCapturing").invoke(sampleFactory.getDeclaredConstructor().newInstance()); assertThat(instance.call(), is(BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testNonCapturingLambdaWithArguments() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Class.forName("java.util.function.Function"))).transform(new SingleMethodReplacer("apply")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); Object instance = sampleFactory.getDeclaredMethod("nonCapturingWithArguments").invoke(sampleFactory.getDeclaredConstructor().newInstance()); assertThat(instance.getClass().getMethod("apply", Object.class).invoke(instance, FOO), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testCapturingLambdaWithArguments() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .type(isSubTypeOf(Class.forName("java.util.function.Function"))).transform(new SingleMethodReplacer("apply")) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); Object instance = sampleFactory.getDeclaredMethod("capturingWithArguments", String.class).invoke(sampleFactory.getDeclaredConstructor().newInstance(), FOO); assertThat(instance.getClass().getMethod("apply", Object.class).invoke(instance, FOO), is((Object) BAR)); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @JavaVersionRule.Enforce(8) @AgentAttachmentRule.Enforce(redefinesClasses = true) @IntegrationRule.Enforce public void testSerializableLambda() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassLoader classLoader = lambdaSamples(); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED) .installOn(ByteBuddyAgent.getInstrumentation()); try { Class<?> sampleFactory = classLoader.loadClass(LAMBDA_SAMPLE_FACTORY); @SuppressWarnings("unchecked") Callable<String> instance = (Callable<String>) sampleFactory.getDeclaredMethod("serializable", String.class).invoke(sampleFactory.getDeclaredConstructor().newInstance(), FOO); assertThat(instance.call(), is(FOO)); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(instance); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray())); @SuppressWarnings("unchecked") Callable<String> deserialized = (Callable<String>) objectInputStream.readObject(); assertThat(deserialized.call(), is(FOO)); objectInputStream.close(); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); AgentBuilder.LambdaInstrumentationStrategy.release(classFileTransformer, ByteBuddyAgent.getInstrumentation()); } } @Test @IntegrationRule.Enforce public void testAdviceTransformer() throws Exception { assertThat(ByteBuddyAgent.install(), instanceOf(Instrumentation.class)); ClassFileTransformer classFileTransformer = new AgentBuilder.Default() .with(poolStrategy) .ignore(none()) .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) .type(ElementMatchers.is(Foo.class), ElementMatchers.is(classLoader)).transform(new AgentBuilder.Transformer.ForAdvice() .with(poolStrategy) .with(AgentBuilder.LocationStrategy.ForClassLoader.STRONG) .include(BarAdvice.class.getClassLoader()) .with(Assigner.DEFAULT) .withExceptionHandler(Removal.SINGLE) .advice(named(FOO), BarAdvice.class.getName())) .installOnByteBuddyAgent(); try { Class<?> type = classLoader.loadClass(Foo.class.getName()); assertThat(type.getDeclaredMethod(FOO).invoke(type.getDeclaredConstructor().newInstance()), is((Object) (FOO + BAR))); } finally { ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer); } } private static class FooTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.method(named(FOO)).intercept(FixedValue.value(BAR)); } } public static class Foo { public String foo() { return FOO; } } public static class Baz { public String foo() { return FOO; } } public static class BarTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { try { return builder.method(named(FOO)).intercept(MethodDelegation.to(new Interceptor())); } catch (Exception exception) { throw new AssertionError(exception); } } public static class Interceptor { public String intercept() { return BAR; } } } public static class Bar { public String foo() { return FOO; } } public static class QuxTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { try { return builder.method(named(FOO)).intercept(MethodDelegation.to(new Interceptor())); } catch (Exception exception) { throw new AssertionError(exception); } } public static class Interceptor { public String intercept(@SuperCall Callable<String> zuper) throws Exception { return zuper.call() + BAR; } } } public static class Qux { public String foo() { return FOO; } } public static class QuxBazTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { try { return builder.method(named(FOO)).intercept(MethodDelegation.to(new Interceptor())); } catch (Exception exception) { throw new AssertionError(exception); } } public static class Interceptor { // Interceptor cannot reference QuxBaz as the system class loader type does not equal the child-first type public String intercept(@Super(proxyType = TargetType.class) Object zuper) throws Exception { return zuper.getClass().getClassLoader().loadClass(QuxBaz.class.getName()).getDeclaredMethod("foo").invoke(zuper) + BAR; } } } public static class QuxBaz { public String foo() { return FOO; } } public static class ConstructorTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.constructor(ElementMatchers.any()).intercept(SuperMethodCall.INSTANCE); } } private static class SingleMethodReplacer implements AgentBuilder.Transformer { private final String methodName; public SingleMethodReplacer(String methodName) { this.methodName = methodName; } @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.method(named(methodName)).intercept(FixedValue.value(BAR)); } } public static class BarAdviceTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.visit(Advice.to(BarAdvice.class).on(named(FOO))); } } public static class QuxAdviceTransformer implements AgentBuilder.Transformer { @Override public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.visit(Advice.to(QuxAdvice.class).on(named(FOO))); } } private static class BarAdvice { @Advice.OnMethodExit private static void exit(@Advice.Return(readOnly = false) String value) { value += BAR; } } private static class QuxAdvice { @Advice.OnMethodExit private static void exit(@Advice.Return(readOnly = false) String value) { value += QUX; } } }