diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngine.java b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngine.java index a126a7ec9a0..faec4bbe793 100644 --- a/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngine.java +++ b/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngine.java @@ -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; @@ -479,8 +478,8 @@ enum Default implements MethodLookupEngine { DEFAULT_LOOKUP_ENABLED { @Override protected Map> apply(MethodBucket methodBucket, - Collection interfaces, - Collection defaultMethodRelevantInterfaces) { + Collection interfaces, + Collection defaultMethodRelevantInterfaces) { interfaces.removeAll(defaultMethodRelevantInterfaces); return Collections.unmodifiableMap(methodBucket.pushInterfacesAndExtractDefaultMethods(defaultMethodRelevantInterfaces)); } @@ -492,8 +491,8 @@ protected Map> apply(MethodBucket method DEFAULT_LOOKUP_DISABLED { @Override protected Map> apply(MethodBucket methodBucket, - Collection interfaces, - Collection defaultMethodRelevantInterfaces) { + Collection interfaces, + Collection defaultMethodRelevantInterfaces) { interfaces.addAll(defaultMethodRelevantInterfaces); return Collections.emptyMap(); } @@ -502,14 +501,12 @@ protected Map> apply(MethodBucket method @Override public Finding process(TypeDescription typeDescription) { MethodBucket methodBucket = new MethodBucket(typeDescription); - Set interfaces = new HashSet(); - TypeList defaultMethodRelevantInterfaces = typeDescription.getInterfaces().asRawTypes(); - GenericTypeDescription currentType = typeDescription; - while ((currentType = currentType.getSuperType()) != null) { - methodBucket.pushClass(currentType.asRawType()); - interfaces.addAll(currentType.getInterfaces().asRawTypes()); + Set interfaces = new HashSet(); + for (GenericTypeDescription superType : typeDescription) { + methodBucket.pushClass(superType); + interfaces.addAll(superType.getInterfaces()); } - Map> defaultMethods = apply(methodBucket, interfaces, defaultMethodRelevantInterfaces); + Map> defaultMethods = apply(methodBucket, interfaces, typeDescription.getInterfaces()); methodBucket.pushInterfaces(interfaces); return new Finding.Default(methodBucket.getTypeOfInterest(), methodBucket.extractInvokableMethods(), @@ -527,8 +524,8 @@ public Finding process(TypeDescription typeDescription) { * @return A map containing all extracted default methods. */ protected abstract Map> apply(MethodBucket methodBucket, - Collection interfaces, - Collection defaultMethodRelevantInterfaces); + Collection interfaces, + Collection defaultMethodRelevantInterfaces); @Override public String toString() { @@ -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); } @@ -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 methodMatcher) { - if (processedTypes.add(typeDescription)) { + private void pushClass(GenericTypeDescription typeDescription, ElementMatcher methodMatcher) { + if (processedTypes.add(typeDescription.asRawType())) { for (MethodDescription methodDescription : typeDescription.getDeclaredMethods().filter(methodMatcher)) { MethodDescription.Token methodToken = methodDescription.asToken(); MethodDescription overridingMethod = classMethods.get(methodToken); @@ -657,7 +654,7 @@ private void pushClass(TypeDescription typeDescription, ElementMatcher typeDescriptions) { + private void pushInterfaces(Collection typeDescriptions) { pushInterfaces(typeDescriptions, DefaultMethodLookup.Disabled.INSTANCE); } @@ -671,7 +668,7 @@ private void pushInterfaces(Collection typeDescriptio * @return A map of all extracted default methods where each interface is mapped to its invokable default * methods. */ - private Map> pushInterfacesAndExtractDefaultMethods(Collection typeDescriptions) { + private Map> pushInterfacesAndExtractDefaultMethods(Collection typeDescriptions) { DefaultMethodLookup.Enabled defaultMethodLookup = new DefaultMethodLookup.Enabled(typeDescriptions); pushInterfaces(typeDescriptions, defaultMethodLookup); return defaultMethodLookup.materialize(); @@ -684,10 +681,10 @@ private Map> pushInterfacesAndExtractDef * filtered automatically. * @param defaultMethodLookup An implementation for looking up default methods for these interfaces. */ - private void pushInterfaces(Collection typeDescriptions, + private void pushInterfaces(Collection typeDescriptions, DefaultMethodLookup defaultMethodLookup) { Set processedMethods = new HashSet(classMethods.keySet()); - for (TypeDescription interfaceTypeDescription : typeDescriptions) { + for (GenericTypeDescription interfaceTypeDescription : typeDescriptions) { pushInterface(interfaceTypeDescription, processedMethods, defaultMethodLookup); } } @@ -728,31 +725,31 @@ private void pushInterfaces(Collection 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 processedMethodsInHierarchy, DefaultMethodLookup defaultMethodLookup) { Set locallyProcessedMethods = new HashSet(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()); } } @@ -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 declaredInterfaceTypes; + private final Collection declaredInterfaceTypes; /** * A mapping of interfaces to the default method that can be invoked on the given interface. @@ -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 declaredInterfaceTypes) { + protected Enabled(Collection declaredInterfaceTypes) { this.declaredInterfaceTypes = declaredInterfaceTypes; defaultMethods = new HashMap>(); methodDeclarations = new HashMap>(); diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngineDefaultTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngineDefaultTest.java index 33d4a69690a..47be940113c 100644 --- a/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngineDefaultTest.java +++ b/byte-buddy-dep/src/test/java/net/bytebuddy/dynamic/scaffold/MethodLookupEngineDefaultTest.java @@ -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.*; @@ -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", @@ -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(); @@ -482,4 +522,22 @@ private static abstract class AbstractAdditionalMethodInterfaceClass implements private static abstract class ConflictingInterfaceClass implements SingleMethodInterface, ConflictingSingleMethodInterface { /* empty */ } + + public static abstract class GenericType { + + abstract List foo(); + + interface GenericInterfaceType { + + List bar(); + } + + static abstract class Resolved extends GenericType implements GenericInterfaceType { + /* empty */ + } + + static abstract class Unresolved extends GenericType implements GenericInterfaceType { + /* empty */ + } + } }