Skip to content

Commit

Permalink
Added further documentation and unit tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rafael Winterhalter committed Apr 13, 2015
1 parent dacb5ec commit 93abc1f
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 49 deletions.
Expand Up @@ -849,7 +849,6 @@ public MethodVisitor visitMethod(int modifiers,
String descriptor, String descriptor,
String genericSignature, String genericSignature,
String[] exceptionTypeInternalName) { String[] exceptionTypeInternalName) {
// TODO: Check if this works for final field assignments inside, maybe cheat with names? Check constructor final reassignment in general
if (internalName.equals(MethodDescription.TYPE_INITIALIZER_INTERNAL_NAME)) { if (internalName.equals(MethodDescription.TYPE_INITIALIZER_INTERNAL_NAME)) {
TypeInitializerInjection injectedCode = new TypeInitializerInjection(); TypeInitializerInjection injectedCode = new TypeInitializerInjection();
this.injectedCode = injectedCode; this.injectedCode = injectedCode;
Expand Down
Expand Up @@ -8,12 +8,30 @@


import static net.bytebuddy.matcher.ElementMatchers.*; import static net.bytebuddy.matcher.ElementMatchers.*;


/**
* A latent method matcher that identifies methods to instrument when redefining or rebasing a type.
*/
public class InlineInstrumentationMatcher implements LatentMethodMatcher { public class InlineInstrumentationMatcher implements LatentMethodMatcher {


/**
* A method matcher that matches any ignored method.
*/
private final ElementMatcher<? super MethodDescription> ignoredMethods; private final ElementMatcher<? super MethodDescription> ignoredMethods;


private final ElementMatcher<MethodDescription> predefinedMethodSignatures; /**
* A method matcher that matches any predefined method.
*/
private final ElementMatcher<? super MethodDescription> predefinedMethodSignatures;


/**
* Creates a matcher where only overridable or declared methods are matched unless those are ignored. Methods that
* are declared by the target type are only matched if they are not ignored. Declared methods that are not found on the
* target type are always matched.
*
* @param ignoredMethods A method matcher that matches any ignored method.
* @param targetType The target type of the instrumentation before adding any user methods.
* @return A latent method matcher that identifies any method to instrument for a rebasement or redefinition.
*/
protected static LatentMethodMatcher of(ElementMatcher<? super MethodDescription> ignoredMethods, TypeDescription targetType) { protected static LatentMethodMatcher of(ElementMatcher<? super MethodDescription> ignoredMethods, TypeDescription targetType) {
ElementMatcher.Junction<MethodDescription> predefinedMethodSignatures = none(); ElementMatcher.Junction<MethodDescription> predefinedMethodSignatures = none();
for (MethodDescription methodDescription : targetType.getDeclaredMethods()) { for (MethodDescription methodDescription : targetType.getDeclaredMethods()) {
Expand All @@ -27,15 +45,41 @@ protected static LatentMethodMatcher of(ElementMatcher<? super MethodDescription
return new InlineInstrumentationMatcher(ignoredMethods, predefinedMethodSignatures); return new InlineInstrumentationMatcher(ignoredMethods, predefinedMethodSignatures);
} }


/**
* Creates a new inline instrumentation matcher.
*
* @param ignoredMethods A method matcher that matches any ignored method.
* @param predefinedMethodSignatures A method matcher that matches any predefined method.
*/
protected InlineInstrumentationMatcher(ElementMatcher<? super MethodDescription> ignoredMethods, protected InlineInstrumentationMatcher(ElementMatcher<? super MethodDescription> ignoredMethods,
ElementMatcher<MethodDescription> predefinedMethodSignatures) { ElementMatcher<? super MethodDescription> predefinedMethodSignatures) {
this.ignoredMethods = ignoredMethods; this.ignoredMethods = ignoredMethods;
this.predefinedMethodSignatures = predefinedMethodSignatures; this.predefinedMethodSignatures = predefinedMethodSignatures;
} }


@Override @Override
public ElementMatcher<? super MethodDescription> resolve(TypeDescription instrumentedType) { public ElementMatcher<? super MethodDescription> resolve(TypeDescription instrumentedType) {
return not(ignoredMethods).and(isOverridable().or(isDeclaredBy(instrumentedType)) return not(ignoredMethods).and(isOverridable().or(isDeclaredBy(instrumentedType)))
.or(isDeclaredBy(instrumentedType).and(not(predefinedMethodSignatures)))); .or(isDeclaredBy(instrumentedType).and(not(predefinedMethodSignatures)));
}

@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& ignoredMethods.equals(((InlineInstrumentationMatcher) other).ignoredMethods)
&& predefinedMethodSignatures.equals(((InlineInstrumentationMatcher) other).predefinedMethodSignatures);
}

@Override
public int hashCode() {
return 31 * ignoredMethods.hashCode() + predefinedMethodSignatures.hashCode();
}

@Override
public String toString() {
return "InlineInstrumentationMatcher{" +
"ignoredMethods=" + ignoredMethods +
", predefinedMethodSignatures=" + predefinedMethodSignatures +
'}';
} }
} }
Expand Up @@ -9,7 +9,6 @@
import net.bytebuddy.instrumentation.type.TypeDescription; import net.bytebuddy.instrumentation.type.TypeDescription;
import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.MethodVisitor;


import java.util.HashSet;
import java.util.List; import java.util.List;


/** /**
Expand Down Expand Up @@ -198,10 +197,22 @@ public String toString() {
*/ */
public static class Factory implements Instrumentation.Target.Factory { public static class Factory implements Instrumentation.Target.Factory {


/**
* The bridge method resolver factory to use.
*/
private final BridgeMethodResolver.Factory bridgeMethodResolverFactory; private final BridgeMethodResolver.Factory bridgeMethodResolverFactory;


/**
* The method rebase resolver to use.
*/
private final MethodRebaseResolver methodRebaseResolver; private final MethodRebaseResolver methodRebaseResolver;


/**
* Creates a new factory for a rebase instrumentation target.
*
* @param bridgeMethodResolverFactory The bridge method resolver factory to use.
* @param methodRebaseResolver The method rebase resolver to use.
*/
public Factory(BridgeMethodResolver.Factory bridgeMethodResolverFactory, MethodRebaseResolver methodRebaseResolver) { public Factory(BridgeMethodResolver.Factory bridgeMethodResolverFactory, MethodRebaseResolver methodRebaseResolver) {
this.bridgeMethodResolverFactory = bridgeMethodResolverFactory; this.bridgeMethodResolverFactory = bridgeMethodResolverFactory;
this.methodRebaseResolver = methodRebaseResolver; this.methodRebaseResolver = methodRebaseResolver;
Expand All @@ -211,5 +222,27 @@ public Factory(BridgeMethodResolver.Factory bridgeMethodResolverFactory, MethodR
public Instrumentation.Target make(MethodLookupEngine.Finding finding, List<? extends MethodDescription> instrumentedMethods) { public Instrumentation.Target make(MethodLookupEngine.Finding finding, List<? extends MethodDescription> instrumentedMethods) {
return new RebaseInstrumentationTarget(finding, bridgeMethodResolverFactory, methodRebaseResolver); return new RebaseInstrumentationTarget(finding, bridgeMethodResolverFactory, methodRebaseResolver);
} }

@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& bridgeMethodResolverFactory.equals(((Factory) other).bridgeMethodResolverFactory)
&& methodRebaseResolver.equals(((Factory) other).methodRebaseResolver);
}

@Override
public int hashCode() {
int result = bridgeMethodResolverFactory.hashCode();
result = 31 * result + methodRebaseResolver.hashCode();
return result;
}

@Override
public String toString() {
return "RebaseInstrumentationTarget.Factory{" +
"bridgeMethodResolverFactory=" + bridgeMethodResolverFactory +
", methodRebaseResolver=" + methodRebaseResolver +
'}';
}
} }
} }
Expand Up @@ -23,9 +23,7 @@
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;


import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy; import static net.bytebuddy.matcher.ElementMatchers.*;
import static net.bytebuddy.matcher.ElementMatchers.isOverridable;
import static net.bytebuddy.matcher.ElementMatchers.not;


/** /**
* Creates a dynamic type on basis of loaded types where the dynamic type extends a given type. * Creates a dynamic type on basis of loaded types where the dynamic type extends a given type.
Expand Down Expand Up @@ -221,7 +219,6 @@ public DynamicType.Unloaded<T> make() {
} }





/** /**
* Applies this builder's constructor strategy to the given instrumented type. * Applies this builder's constructor strategy to the given instrumented type.
* *
Expand All @@ -239,17 +236,82 @@ private InstrumentedType applyConstructorStrategy(InstrumentedType instrumentedT
return instrumentedType; return instrumentedType;
} }


@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& super.equals(other)
&& constructorStrategy.equals(((SubclassDynamicTypeBuilder<?>) other).constructorStrategy);
}

@Override
public int hashCode() {
return 31 * super.hashCode() + constructorStrategy.hashCode();
}

@Override
public String toString() {
return "SubclassDynamicTypeBuilder{" +
"classFileVersion=" + classFileVersion +
", namingStrategy=" + namingStrategy +
", auxiliaryTypeNamingStrategy=" + auxiliaryTypeNamingStrategy +
", targetType=" + targetType +
", interfaceTypes=" + interfaceTypes +
", modifiers=" + modifiers +
", attributeAppender=" + attributeAppender +
", ignoredMethods=" + ignoredMethods +
", bridgeMethodResolverFactory=" + bridgeMethodResolverFactory +
", classVisitorWrapperChain=" + classVisitorWrapperChain +
", fieldRegistry=" + fieldRegistry +
", methodRegistry=" + methodRegistry +
", methodLookupEngineFactory=" + methodLookupEngineFactory +
", defaultFieldAttributeAppenderFactory=" + defaultFieldAttributeAppenderFactory +
", defaultMethodAttributeAppenderFactory=" + defaultMethodAttributeAppenderFactory +
", fieldTokens=" + fieldTokens +
", methodTokens=" + methodTokens +
", constructorStrategy=" + constructorStrategy +
'}';
}

/**
* A matcher that locates all methods that are overridable and not ignored or that are directly defined on the instrumented type.
*/
protected static class InstrumentableMatcher implements LatentMethodMatcher { protected static class InstrumentableMatcher implements LatentMethodMatcher {


/**
* A matcher for the ignored methods.
*/
private final ElementMatcher<? super MethodDescription> ignoredMethods; private final ElementMatcher<? super MethodDescription> ignoredMethods;


public InstrumentableMatcher(ElementMatcher<? super MethodDescription> ignoredMethods) { /**
* Creates a latent method matcher that matches all methods that are to be instrumented by a {@link SubclassDynamicTypeBuilder}.
*
* @param ignoredMethods A matcher for the ignored methods.
*/
protected InstrumentableMatcher(ElementMatcher<? super MethodDescription> ignoredMethods) {
this.ignoredMethods = ignoredMethods; this.ignoredMethods = ignoredMethods;
} }


@Override @Override
public ElementMatcher<? super MethodDescription> resolve(TypeDescription instrumentedType) { public ElementMatcher<? super MethodDescription> resolve(TypeDescription instrumentedType) {
return isOverridable().and(not(ignoredMethods)).or(isDeclaredBy(instrumentedType)); return isOverridable().and(not(ignoredMethods)).or(isDeclaredBy(instrumentedType));
} }

@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass())
&& ignoredMethods.equals(((InstrumentableMatcher) other).ignoredMethods);
}

@Override
public int hashCode() {
return ignoredMethods.hashCode();
}

@Override
public String toString() {
return "SubclassDynamicTypeBuilder.InstrumentableMatcher{" +
"ignoredMethods=" + ignoredMethods +
'}';
}
} }
} }
@@ -0,0 +1,90 @@
package net.bytebuddy.dynamic.scaffold.inline;

import net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder;
import net.bytebuddy.instrumentation.method.MethodDescription;
import net.bytebuddy.instrumentation.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.LatentMethodMatcher;
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 static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.when;

public class InlineInstrumentationMatcherTest {

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

@Mock
private MethodDescription methodDescription;

@Mock
private TypeDescription typeDescription, otherType;

@Mock
private ElementMatcher<? super MethodDescription> ignoredMethods, predefinedMethods;

private LatentMethodMatcher latentMethodMatcher;

@Before
public void setUp() throws Exception {
latentMethodMatcher = new InlineInstrumentationMatcher(ignoredMethods, predefinedMethods);
}

@Test
public void testMatchesOverridable() throws Exception {
when(methodDescription.isOverridable()).thenReturn(true);
when(ignoredMethods.matches(methodDescription)).thenReturn(false);
when(predefinedMethods.matches(methodDescription)).thenReturn(false);
when(methodDescription.getDeclaringType()).thenReturn(otherType);
assertThat(latentMethodMatcher.resolve(typeDescription).matches(methodDescription), is(true));
}

@Test
public void testMatchesDeclaredNotTargetType() throws Exception {
when(methodDescription.isOverridable()).thenReturn(false);
when(ignoredMethods.matches(methodDescription)).thenReturn(false);
when(predefinedMethods.matches(methodDescription)).thenReturn(false);
when(methodDescription.getDeclaringType()).thenReturn(typeDescription);
assertThat(latentMethodMatcher.resolve(typeDescription).matches(methodDescription), is(true));
}

@Test
public void testMatchesDeclaredButIgnoredNotPredefined() throws Exception {
when(methodDescription.isOverridable()).thenReturn(false);
when(ignoredMethods.matches(methodDescription)).thenReturn(true);
when(predefinedMethods.matches(methodDescription)).thenReturn(false);
when(methodDescription.getDeclaringType()).thenReturn(typeDescription);
assertThat(latentMethodMatcher.resolve(typeDescription).matches(methodDescription), is(true));
}

@Test
public void testMatchesDeclaredButIgnoredPredefined() throws Exception {
when(methodDescription.isOverridable()).thenReturn(false);
when(ignoredMethods.matches(methodDescription)).thenReturn(true);
when(predefinedMethods.matches(methodDescription)).thenReturn(true);
when(methodDescription.getDeclaringType()).thenReturn(typeDescription);
assertThat(latentMethodMatcher.resolve(typeDescription).matches(methodDescription), is(false));
}

@Test
public void testNotMatchesOverridableIgnored() throws Exception {
when(methodDescription.isOverridable()).thenReturn(true);
when(ignoredMethods.matches(methodDescription)).thenReturn(true);
when(predefinedMethods.matches(methodDescription)).thenReturn(false);
when(methodDescription.getDeclaringType()).thenReturn(otherType);
assertThat(latentMethodMatcher.resolve(typeDescription).matches(methodDescription), is(false));
}

@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(InlineInstrumentationMatcher.class).apply();
}
}
Expand Up @@ -7,8 +7,10 @@
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.StubMethod; import net.bytebuddy.instrumentation.StubMethod;
import net.bytebuddy.instrumentation.SuperMethodCall; import net.bytebuddy.instrumentation.SuperMethodCall;
import net.bytebuddy.instrumentation.method.MethodList;
import net.bytebuddy.pool.TypePool; import net.bytebuddy.pool.TypePool;
import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.test.utility.JavaVersionRule;
import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
Expand All @@ -20,6 +22,8 @@
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URL; import java.net.URL;
import java.net.URLClassLoader; import java.net.URLClassLoader;
import java.util.Collections;
import java.util.List;


import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.any;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
Expand Down Expand Up @@ -154,4 +158,14 @@ public void testParameterMetaDataRetention() throws Exception {
assertThat(getModifiers.invoke(methodParameter[1]), is((Object) 0)); assertThat(getModifiers.invoke(methodParameter[1]), is((Object) 0));
assertThat(getModifiers.invoke(methodParameter[2]), is((Object) 0)); assertThat(getModifiers.invoke(methodParameter[2]), is((Object) 0));
} }

@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(RebaseDynamicTypeBuilder.class).create(new ObjectPropertyAssertion.Creator<List<?>>() {
@Override
public List<?> create() {
return Collections.singletonList(new Object());
}
}).apply();
}
} }

0 comments on commit 93abc1f

Please sign in to comment.