Skip to content

Commit

Permalink
Reworked method lookup engine to extract generic methods instead of n…
Browse files Browse the repository at this point in the history
…on-generic methods.
  • Loading branch information
raphw committed Jul 12, 2015
1 parent 6106f42 commit 9e24665
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 32 deletions.
Expand Up @@ -5,7 +5,6 @@
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.method.ParameterList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import net.bytebuddy.description.type.generic.GenericTypeDescription;
import net.bytebuddy.description.type.generic.GenericTypeList;
import net.bytebuddy.matcher.ElementMatcher;
Expand Down Expand Up @@ -479,8 +478,8 @@ enum Default implements MethodLookupEngine {
DEFAULT_LOOKUP_ENABLED {
@Override
protected Map<TypeDescription, Set<MethodDescription>> apply(MethodBucket methodBucket,
Collection<TypeDescription> interfaces,
Collection<TypeDescription> defaultMethodRelevantInterfaces) {
Collection<GenericTypeDescription> interfaces,
Collection<GenericTypeDescription> defaultMethodRelevantInterfaces) {
interfaces.removeAll(defaultMethodRelevantInterfaces);
return Collections.unmodifiableMap(methodBucket.pushInterfacesAndExtractDefaultMethods(defaultMethodRelevantInterfaces));
}
Expand All @@ -492,8 +491,8 @@ protected Map<TypeDescription, Set<MethodDescription>> apply(MethodBucket method
DEFAULT_LOOKUP_DISABLED {
@Override
protected Map<TypeDescription, Set<MethodDescription>> apply(MethodBucket methodBucket,
Collection<TypeDescription> interfaces,
Collection<TypeDescription> defaultMethodRelevantInterfaces) {
Collection<GenericTypeDescription> interfaces,
Collection<GenericTypeDescription> defaultMethodRelevantInterfaces) {
interfaces.addAll(defaultMethodRelevantInterfaces);
return Collections.emptyMap();
}
Expand All @@ -502,14 +501,12 @@ protected Map<TypeDescription, Set<MethodDescription>> apply(MethodBucket method
@Override
public Finding process(TypeDescription typeDescription) {
MethodBucket methodBucket = new MethodBucket(typeDescription);
Set<TypeDescription> interfaces = new HashSet<TypeDescription>();
TypeList defaultMethodRelevantInterfaces = typeDescription.getInterfaces().asRawTypes();
GenericTypeDescription currentType = typeDescription;
while ((currentType = currentType.getSuperType()) != null) {
methodBucket.pushClass(currentType.asRawType());
interfaces.addAll(currentType.getInterfaces().asRawTypes());
Set<GenericTypeDescription> interfaces = new HashSet<GenericTypeDescription>();
for (GenericTypeDescription superType : typeDescription) {
methodBucket.pushClass(superType);
interfaces.addAll(superType.getInterfaces());
}
Map<TypeDescription, Set<MethodDescription>> defaultMethods = apply(methodBucket, interfaces, defaultMethodRelevantInterfaces);
Map<TypeDescription, Set<MethodDescription>> defaultMethods = apply(methodBucket, interfaces, typeDescription.getInterfaces());
methodBucket.pushInterfaces(interfaces);
return new Finding.Default(methodBucket.getTypeOfInterest(),
methodBucket.extractInvokableMethods(),
Expand All @@ -527,8 +524,8 @@ public Finding process(TypeDescription typeDescription) {
* @return A map containing all extracted default methods.
*/
protected abstract Map<TypeDescription, Set<MethodDescription>> apply(MethodBucket methodBucket,
Collection<TypeDescription> interfaces,
Collection<TypeDescription> defaultMethodRelevantInterfaces);
Collection<GenericTypeDescription> interfaces,
Collection<GenericTypeDescription> defaultMethodRelevantInterfaces);

@Override
public String toString() {
Expand Down Expand Up @@ -625,7 +622,7 @@ protected MethodBucket(TypeDescription typeOfInterest) {
*
* @param typeDescription The class for which all virtual members are to be extracted.
*/
private void pushClass(TypeDescription typeDescription) {
private void pushClass(GenericTypeDescription typeDescription) {
pushClass(typeDescription, virtualMethodMatcher);
}

Expand All @@ -638,8 +635,8 @@ private void pushClass(TypeDescription typeDescription) {
* @param methodMatcher The method matcher for filtering methods of interest that are declared by the
* given type.
*/
private void pushClass(TypeDescription typeDescription, ElementMatcher<? super MethodDescription> methodMatcher) {
if (processedTypes.add(typeDescription)) {
private void pushClass(GenericTypeDescription typeDescription, ElementMatcher<? super MethodDescription> methodMatcher) {
if (processedTypes.add(typeDescription.asRawType())) {
for (MethodDescription methodDescription : typeDescription.getDeclaredMethods().filter(methodMatcher)) {
MethodDescription.Token methodToken = methodDescription.asToken();
MethodDescription overridingMethod = classMethods.get(methodToken);
Expand All @@ -657,7 +654,7 @@ private void pushClass(TypeDescription typeDescription, ElementMatcher<? super M
* @param typeDescriptions A collection of interfaces to push into the bucket. Duplicates will be
* filtered automatically.
*/
private void pushInterfaces(Collection<? extends TypeDescription> typeDescriptions) {
private void pushInterfaces(Collection<? extends GenericTypeDescription> typeDescriptions) {
pushInterfaces(typeDescriptions, DefaultMethodLookup.Disabled.INSTANCE);
}

Expand All @@ -671,7 +668,7 @@ private void pushInterfaces(Collection<? extends TypeDescription> typeDescriptio
* @return A map of all extracted default methods where each interface is mapped to its invokable default
* methods.
*/
private Map<TypeDescription, Set<MethodDescription>> pushInterfacesAndExtractDefaultMethods(Collection<? extends TypeDescription> typeDescriptions) {
private Map<TypeDescription, Set<MethodDescription>> pushInterfacesAndExtractDefaultMethods(Collection<? extends GenericTypeDescription> typeDescriptions) {
DefaultMethodLookup.Enabled defaultMethodLookup = new DefaultMethodLookup.Enabled(typeDescriptions);
pushInterfaces(typeDescriptions, defaultMethodLookup);
return defaultMethodLookup.materialize();
Expand All @@ -684,10 +681,10 @@ private Map<TypeDescription, Set<MethodDescription>> pushInterfacesAndExtractDef
* filtered automatically.
* @param defaultMethodLookup An implementation for looking up default methods for these interfaces.
*/
private void pushInterfaces(Collection<? extends TypeDescription> typeDescriptions,
private void pushInterfaces(Collection<? extends GenericTypeDescription> typeDescriptions,
DefaultMethodLookup defaultMethodLookup) {
Set<MethodDescription.Token> processedMethods = new HashSet<MethodDescription.Token>(classMethods.keySet());
for (TypeDescription interfaceTypeDescription : typeDescriptions) {
for (GenericTypeDescription interfaceTypeDescription : typeDescriptions) {
pushInterface(interfaceTypeDescription, processedMethods, defaultMethodLookup);
}
}
Expand Down Expand Up @@ -728,31 +725,31 @@ private void pushInterfaces(Collection<? extends TypeDescription> typeDescriptio
* again since it was already marked as processed by adding it to
* {@link MethodLookupEngine.Default.MethodBucket#processedTypes}.
*
* @param typeDescription The interface type to process.
* @param defaultMethodLookup A processor for performing a lookup of default methods.
* @param typeDescription The interface type to process.
* @param defaultMethodLookup A processor for performing a lookup of default methods.
*/
private void pushInterface(TypeDescription typeDescription,
private void pushInterface(GenericTypeDescription typeDescription,
Set<MethodDescription.Token> processedMethodsInHierarchy,
DefaultMethodLookup defaultMethodLookup) {
Set<MethodDescription.Token> locallyProcessedMethods = new HashSet<MethodDescription.Token>(processedMethodsInHierarchy);
if (processedTypes.add(typeDescription)) {
defaultMethodLookup.begin(typeDescription);
if (processedTypes.add(typeDescription.asRawType())) {
defaultMethodLookup.begin(typeDescription.asRawType());
for (MethodDescription methodDescription : typeDescription.getDeclaredMethods().filter(virtualMethodMatcher)) {
MethodDescription.Token methodToken = methodDescription.asToken();
if (locallyProcessedMethods.add(methodToken)) {
MethodDescription conflictingMethod = interfaceMethods.get(methodToken);
MethodDescription resolvedMethod = methodDescription;
if (conflictingMethod != null && !conflictingMethod.getDeclaringType().asRawType().isAssignableFrom(typeDescription)) {
if (conflictingMethod != null && !conflictingMethod.getDeclaringType().asRawType().isAssignableFrom(typeDescription.asRawType())) {
resolvedMethod = ConflictingInterfaceMethod.of(typeOfInterest, conflictingMethod, methodDescription);
}
interfaceMethods.put(methodToken, resolvedMethod);
}
defaultMethodLookup.register(methodDescription);
}
for (TypeDescription interfaceType : typeDescription.getInterfaces().asRawTypes()) {
for (GenericTypeDescription interfaceType : typeDescription.getInterfaces()) {
pushInterface(interfaceType, locallyProcessedMethods, defaultMethodLookup);
}
defaultMethodLookup.complete(typeDescription);
defaultMethodLookup.complete(typeDescription.asRawType());
}
}

Expand Down Expand Up @@ -875,7 +872,7 @@ class Enabled implements MethodBucket.DefaultMethodLookup {
* The declared interfaces of an instrumented type that are to be extracted by this default
* method lookup.
*/
private final Collection<? extends TypeDescription> declaredInterfaceTypes;
private final Collection<? extends GenericTypeDescription> declaredInterfaceTypes;

/**
* A mapping of interfaces to the default method that can be invoked on the given interface.
Expand All @@ -894,7 +891,7 @@ class Enabled implements MethodBucket.DefaultMethodLookup {
* @param declaredInterfaceTypes The interfaces that were declared by a type and that
* should finally be extracted by this default method lookup.
*/
protected Enabled(Collection<? extends TypeDescription> declaredInterfaceTypes) {
protected Enabled(Collection<? extends GenericTypeDescription> declaredInterfaceTypes) {
this.declaredInterfaceTypes = declaredInterfaceTypes;
defaultMethods = new HashMap<TypeDescription, Set<MethodDescription>>();
methodDeclarations = new HashMap<TypeDescription, Set<MethodDescription.Token>>();
Expand Down
Expand Up @@ -15,6 +15,7 @@
import org.junit.Test;
import org.junit.rules.MethodRule;

import java.util.List;
import java.util.Set;

import static net.bytebuddy.matcher.ElementMatchers.*;
Expand All @@ -28,7 +29,7 @@

public class MethodLookupEngineDefaultTest {

private static final String TO_STRING = "toString", FOO = "foo", PREFIX = "net.bytebuddy.test.precompiled.";
private static final String TO_STRING = "toString", FOO = "foo", BAR = "bar", PREFIX = "net.bytebuddy.test.precompiled.";

private static final String SINGLE_DEFAULT_METHOD_ABSTRACT_OVERRIDING_CLASS = PREFIX + "SingleDefaultMethodAbstractOverridingClass",
SINGLE_DEFAULT_METHOD_ABSTRACT_OVERRIDING_INTERFACE = PREFIX + "SingleDefaultMethodAbstractOverridingInterface",
Expand Down Expand Up @@ -390,6 +391,45 @@ public void testAmbiguousManifestOverridingDefaultMethodLookup() throws Exceptio
hasItem(singleDefaultMethodManifestOverridingInterface.getDeclaredMethods().getOnly()));
}

@Test
public void testResolvedGenericType() throws Exception {
MethodLookupEngine.Finding finding = methodLookupEngine.process(new TypeDescription.ForLoadedType(GenericType.Resolved.class));
assertThat(finding.getInvokableMethods().filter(named(FOO)).size(), is(1));
GenericTypeDescription foo = finding.getInvokableMethods().filter(named(FOO)).getOnly().getReturnType();
assertThat(foo.getSort(), is(GenericTypeDescription.Sort.PARAMETERIZED));
assertThat(foo.asRawType().represents(List.class), is(true));
assertThat(foo.getParameters().size(), is(1));
assertThat(foo.getParameters().getOnly().getSort(), is(GenericTypeDescription.Sort.NON_GENERIC));
assertThat(foo.getParameters().getOnly().asRawType().represents(String.class), is(true));
assertThat(finding.getInvokableMethods().filter(named(BAR)).size(), is(1));
GenericTypeDescription bar = finding.getInvokableMethods().filter(named(BAR)).getOnly().getReturnType();
assertThat(bar.getSort(), is(GenericTypeDescription.Sort.PARAMETERIZED));
assertThat(bar.asRawType().represents(List.class), is(true));
assertThat(bar.getParameters().size(), is(1));
assertThat(bar.getParameters().getOnly().getSort(), is(GenericTypeDescription.Sort.NON_GENERIC));
assertThat(bar.getParameters().getOnly().asRawType().represents(String.class), is(true));
}

@Test
public void testUnresolvedGenericType() throws Exception {
TypeDescription unresolvedType = new TypeDescription.ForLoadedType(GenericType.Unresolved.class);
MethodLookupEngine.Finding finding = methodLookupEngine.process(unresolvedType);
assertThat(finding.getInvokableMethods().filter(named(FOO)).size(), is(1));
GenericTypeDescription foo = finding.getInvokableMethods().filter(named(FOO)).getOnly().getReturnType();
assertThat(foo.getSort(), is(GenericTypeDescription.Sort.PARAMETERIZED));
assertThat(foo.asRawType().represents(List.class), is(true));
assertThat(foo.getParameters().size(), is(1));
assertThat(foo.getParameters().getOnly().getSort(), is(GenericTypeDescription.Sort.VARIABLE));
assertThat(foo.getParameters().getOnly(), is(unresolvedType.getTypeVariables().getOnly()));
assertThat(finding.getInvokableMethods().filter(named(BAR)).size(), is(1));
GenericTypeDescription bar = finding.getInvokableMethods().filter(named(BAR)).getOnly().getReturnType();
assertThat(bar.getSort(), is(GenericTypeDescription.Sort.PARAMETERIZED));
assertThat(bar.asRawType().represents(List.class), is(true));
assertThat(bar.getParameters().size(), is(1));
assertThat(bar.getParameters().getOnly().getSort(), is(GenericTypeDescription.Sort.VARIABLE));
assertThat(bar.getParameters().getOnly(), is(unresolvedType.getTypeVariables().getOnly()));
}

@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(MethodLookupEngine.Default.class).apply();
Expand Down Expand Up @@ -482,4 +522,22 @@ private static abstract class AbstractAdditionalMethodInterfaceClass implements
private static abstract class ConflictingInterfaceClass implements SingleMethodInterface, ConflictingSingleMethodInterface {
/* empty */
}

public static abstract class GenericType<T> {

abstract List<T> foo();

interface GenericInterfaceType<S> {

List<S> bar();
}

static abstract class Resolved extends GenericType<String> implements GenericInterfaceType<String> {
/* empty */
}

static abstract class Unresolved<S> extends GenericType<S> implements GenericInterfaceType<S> {
/* empty */
}
}
}

0 comments on commit 9e24665

Please sign in to comment.