Skip to content

Commit

Permalink
Added option to not use the reflection API when encountering loaded t…
Browse files Browse the repository at this point in the history
…ypes during a retransformation or redefinition of a type but rather to rely onto the type pool-generated description.
  • Loading branch information
raphw committed Jun 14, 2016
1 parent b365f38 commit fd623d9
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 29 deletions.

Large diffs are not rendered by default.

Expand Up @@ -7283,6 +7283,22 @@ public ForLoadedType(Class<?> type) {
this.type = type;
}

/**
* Returns the type's actual name where it is taken into consideration that this type might be loaded anonymously.
* In this case, the remainder of the types name is suffixed by {@code /<id>} which is removed when using this method
* but is retained when calling {@link Class#getName()}.
*
* @param type The type for which to resolve its name.
* @return The type's actual name.
*/
public static String getName(Class<?> type) {
String name = type.getName();
int anonymousLoaderIndex = name.indexOf('/');
return anonymousLoaderIndex == -1
? name
: name.substring(0, anonymousLoaderIndex);
}

@Override
public boolean isAssignableFrom(Class<?> type) {
// The JVM conducts more efficient assignability lookups of loaded types what is attempted first.
Expand Down Expand Up @@ -7429,11 +7445,7 @@ public StackSize getStackSize() {

@Override
public String getName() {
String name = type.getName();
int anonymousLoaderIndex = name.indexOf('/');
return anonymousLoaderIndex == -1
? name
: name.substring(0, anonymousLoaderIndex);
return getName(type);
}

@Override
Expand Down
Expand Up @@ -217,6 +217,34 @@ public void testRedefinition() throws Exception {
}
}

@Test
@AgentAttachmentRule.Enforce(redefinesClasses = true)
public void testRedefinitionWithPoolOnly() throws Exception {
// As documented, the class loading type locator is not applicable for redefinitions.
if (typeLocator.equals(AgentBuilder.TypeLocator.ClassLoading.INSTANCE)) {
return;
}
// 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 whz 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(typeLocator)
.ignore(none())
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.DescriptionStrategy.POOL_ONLY)
.type(isAnnotatedWith(ShouldRebase.class), ElementMatchers.is(classLoader)).transform(new FooTransformer())
.installOnByteBuddyAgent();
try {
Class<?> type = classLoader.loadClass(SimpleType.class.getName());
assertThat(type.getDeclaredMethod(FOO).invoke(type.newInstance()), is((Object) BAR));
} finally {
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
}
}

@Test
@AgentAttachmentRule.Enforce(retransformsClasses = true)
public void testRetransformation() throws Exception {
Expand All @@ -240,6 +268,30 @@ public void testRetransformation() throws Exception {
}
}

@Test
@AgentAttachmentRule.Enforce(retransformsClasses = true)
public void testRetransformationWithPoolOnly() 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 whz 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(typeLocator)
.ignore(none())
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.DescriptionStrategy.POOL_ONLY)
.type(isAnnotatedWith(ShouldRebase.class), ElementMatchers.is(classLoader)).transform(new FooTransformer())
.installOnByteBuddyAgent();
try {
Class<?> type = classLoader.loadClass(SimpleType.class.getName());
assertThat(type.getDeclaredMethod(FOO).invoke(type.newInstance()), is((Object) BAR));
} finally {
ByteBuddyAgent.getInstrumentation().removeTransformer(classFileTransformer);
}
}

@Test
@AgentAttachmentRule.Enforce
public void testChainedAgent() throws Exception {
Expand Down
Expand Up @@ -1028,6 +1028,7 @@ public void testExecutingTransformerHandlesNullValue() throws Exception {
accessControlContext,
initializationStrategy,
mock(AgentBuilder.Default.BootstrapInjectionStrategy.class),
AgentBuilder.DescriptionStrategy.HYBRID,
mock(AgentBuilder.RawMatcher.class),
mock(AgentBuilder.Default.Transformation.class))
.transform(mock(ClassLoader.class),
Expand Down
@@ -0,0 +1,88 @@
package net.bytebuddy.agent.builder;

import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.pool.TypePool;
import net.bytebuddy.test.utility.MockitoRule;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.Mock;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.when;

public class AgentBuilderDescriptionStrategyTest {

@Rule
public TestRule mockitoRule = new MockitoRule(this);

@Mock
private AgentBuilder.TypeLocator typeLocator;

@Mock
private TypePool typePool;

@Mock
private TypeDescription typeDescription;

@Test
public void testDescriptionHybridWithLoaded() throws Exception {
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(Object.class.getClassLoader());
when(typeLocator.typePool(classFileLocator, Object.class.getClassLoader())).thenReturn(typePool);
when(typePool.describe(Object.class.getName())).thenReturn(new TypePool.Resolution.Simple(typeDescription));
TypeDescription typeDescription = AgentBuilder.DescriptionStrategy.HYBRID.apply(Object.class.getName(),
Object.class,
typeLocator,
Object.class.getClassLoader(),
classFileLocator);
assertThat(typeDescription, is(TypeDescription.OBJECT));
assertThat(typeDescription, instanceOf(TypeDescription.ForLoadedType.class));
}

@Test
public void testDescriptionHybridWithoutLoaded() throws Exception {
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(Object.class.getClassLoader());
when(typeLocator.typePool(classFileLocator, Object.class.getClassLoader())).thenReturn(typePool);
when(typePool.describe(Object.class.getName())).thenReturn(new TypePool.Resolution.Simple(typeDescription));
TypeDescription typeDescription = AgentBuilder.DescriptionStrategy.HYBRID.apply(Object.class.getName(),
null,
typeLocator,
Object.class.getClassLoader(),
classFileLocator);
assertThat(typeDescription, is(this.typeDescription));
}

@Test
public void testDescriptionPoolOnly() throws Exception {
ClassFileLocator classFileLocator = ClassFileLocator.ForClassLoader.of(Object.class.getClassLoader());
when(typeLocator.typePool(classFileLocator, Object.class.getClassLoader())).thenReturn(typePool);
when(typePool.describe(Object.class.getName())).thenReturn(new TypePool.Resolution.Simple(typeDescription));
assertThat(AgentBuilder.DescriptionStrategy.POOL_ONLY.apply(Object.class.getName(),
Object.class,
typeLocator,
Object.class.getClassLoader(),
classFileLocator), is(typeDescription));
}

@Test
public void testLoadedDescriptionHybrid() throws Exception {
assertThat(AgentBuilder.DescriptionStrategy.HYBRID.apply(Object.class, typeLocator), is(TypeDescription.OBJECT));
assertThat(AgentBuilder.DescriptionStrategy.HYBRID.apply(Object.class, typeLocator), instanceOf(TypeDescription.ForLoadedType.class));
}

@Test
public void testLoadedDescriptionPoolOnly() throws Exception {
when(typeLocator.typePool(ClassFileLocator.ForClassLoader.of(Object.class.getClassLoader()), Object.class.getClassLoader())).thenReturn(typePool);
when(typePool.describe(Object.class.getName())).thenReturn(new TypePool.Resolution.Simple(typeDescription));
assertThat(AgentBuilder.DescriptionStrategy.POOL_ONLY.apply(Object.class, typeLocator), is(typeDescription));
}

@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(AgentBuilder.DescriptionStrategy.class).apply();
}
}
Expand Up @@ -8,6 +8,9 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class TypeDescriptionForLoadedTypeTest extends AbstractTypeDescriptionTest {

@Override
Expand Down Expand Up @@ -121,4 +124,9 @@ public void testGenericInnerTypeAnnotationReceiverTypeOnMethod() throws Exceptio
public void testTypeAnnotationNonGenericInnerType() throws Exception {
super.testTypeAnnotationNonGenericInnerType();
}

@Test
public void testNameEqualityNonAnonymous() throws Exception {
assertThat(TypeDescription.ForLoadedType.getName(Object.class), is(Object.class.getName()));
}
}

0 comments on commit fd623d9

Please sign in to comment.