8029674: (reflect) getMethods returns default methods that are not members of the class
Filter out methods that have a more specific default method from result Reviewed-by: darcy, dlsmith
This commit is contained in:
parent
1694fa432e
commit
6445980a62
@ -2696,12 +2696,26 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
|
||||
static class MethodArray {
|
||||
// Don't add or remove methods except by add() or remove() calls.
|
||||
private Method[] methods;
|
||||
private int length;
|
||||
private int defaults;
|
||||
|
||||
MethodArray() {
|
||||
methods = new Method[20];
|
||||
this(20);
|
||||
}
|
||||
|
||||
MethodArray(int initialSize) {
|
||||
if (initialSize < 2)
|
||||
throw new IllegalArgumentException("Size should be 2 or more");
|
||||
|
||||
methods = new Method[initialSize];
|
||||
length = 0;
|
||||
defaults = 0;
|
||||
}
|
||||
|
||||
boolean hasDefaults() {
|
||||
return defaults != 0;
|
||||
}
|
||||
|
||||
void add(Method m) {
|
||||
@ -2709,6 +2723,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||
methods = Arrays.copyOf(methods, 2 * methods.length);
|
||||
}
|
||||
methods[length++] = m;
|
||||
|
||||
if (m != null && m.isDefault())
|
||||
defaults++;
|
||||
}
|
||||
|
||||
void addAll(Method[] ma) {
|
||||
@ -2742,7 +2759,10 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
}
|
||||
|
||||
void addAllNonStatic(Method[] methods) {
|
||||
/* Add Methods declared in an interface to this MethodArray.
|
||||
* Static methods declared in interfaces are not inherited.
|
||||
*/
|
||||
void addInterfaceMethods(Method[] methods) {
|
||||
for (Method candidate : methods) {
|
||||
if (!Modifier.isStatic(candidate.getModifiers())) {
|
||||
add(candidate);
|
||||
@ -2758,19 +2778,35 @@ public final class Class<T> implements java.io.Serializable,
|
||||
return methods[i];
|
||||
}
|
||||
|
||||
void removeByNameAndSignature(Method toRemove) {
|
||||
Method getFirst() {
|
||||
for (Method m : methods)
|
||||
if (m != null)
|
||||
return m;
|
||||
return null;
|
||||
}
|
||||
|
||||
void removeByNameAndDescriptor(Method toRemove) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
Method m = methods[i];
|
||||
if (m != null &&
|
||||
m.getReturnType() == toRemove.getReturnType() &&
|
||||
m.getName() == toRemove.getName() &&
|
||||
arrayContentsEq(m.getParameterTypes(),
|
||||
toRemove.getParameterTypes())) {
|
||||
methods[i] = null;
|
||||
if (m != null && matchesNameAndDescriptor(m, toRemove)) {
|
||||
remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(int i) {
|
||||
if (methods[i] != null && methods[i].isDefault())
|
||||
defaults--;
|
||||
methods[i] = null;
|
||||
}
|
||||
|
||||
private boolean matchesNameAndDescriptor(Method m1, Method m2) {
|
||||
return m1.getReturnType() == m2.getReturnType() &&
|
||||
m1.getName() == m2.getName() && // name is guaranteed to be interned
|
||||
arrayContentsEq(m1.getParameterTypes(),
|
||||
m2.getParameterTypes());
|
||||
}
|
||||
|
||||
void compactAndTrim() {
|
||||
int newPos = 0;
|
||||
// Get rid of null slots
|
||||
@ -2788,9 +2824,48 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
}
|
||||
|
||||
/* Removes all Methods from this MethodArray that have a more specific
|
||||
* default Method in this MethodArray.
|
||||
*
|
||||
* Users of MethodArray are responsible for pruning Methods that have
|
||||
* a more specific <em>concrete</em> Method.
|
||||
*/
|
||||
void removeLessSpecifics() {
|
||||
if (!hasDefaults())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
Method m = get(i);
|
||||
if (m == null || !m.isDefault())
|
||||
continue;
|
||||
|
||||
for (int j = 0; j < length; j++) {
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
Method candidate = get(j);
|
||||
if (candidate == null)
|
||||
continue;
|
||||
|
||||
if (!matchesNameAndDescriptor(m, candidate))
|
||||
continue;
|
||||
|
||||
if (hasMoreSpecificClass(m, candidate))
|
||||
remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Method[] getArray() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
// Returns true if m1 is more specific than m2
|
||||
static boolean hasMoreSpecificClass(Method m1, Method m2) {
|
||||
Class<?> m1Class = m1.getDeclaringClass();
|
||||
Class<?> m2Class = m2.getDeclaringClass();
|
||||
return m1Class != m2Class && m2Class.isAssignableFrom(m1Class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2819,7 +2894,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
// the end.
|
||||
MethodArray inheritedMethods = new MethodArray();
|
||||
for (Class<?> i : getInterfaces()) {
|
||||
inheritedMethods.addAllNonStatic(i.privateGetPublicMethods());
|
||||
inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods());
|
||||
}
|
||||
if (!isInterface()) {
|
||||
Class<?> c = getSuperclass();
|
||||
@ -2830,8 +2905,10 @@ public final class Class<T> implements java.io.Serializable,
|
||||
// interface methods
|
||||
for (int i = 0; i < supers.length(); i++) {
|
||||
Method m = supers.get(i);
|
||||
if (m != null && !Modifier.isAbstract(m.getModifiers())) {
|
||||
inheritedMethods.removeByNameAndSignature(m);
|
||||
if (m != null &&
|
||||
!Modifier.isAbstract(m.getModifiers()) &&
|
||||
!m.isDefault()) {
|
||||
inheritedMethods.removeByNameAndDescriptor(m);
|
||||
}
|
||||
}
|
||||
// Insert superclass's inherited methods before
|
||||
@ -2844,9 +2921,10 @@ public final class Class<T> implements java.io.Serializable,
|
||||
// Filter out all local methods from inherited ones
|
||||
for (int i = 0; i < methods.length(); i++) {
|
||||
Method m = methods.get(i);
|
||||
inheritedMethods.removeByNameAndSignature(m);
|
||||
inheritedMethods.removeByNameAndDescriptor(m);
|
||||
}
|
||||
methods.addAllIfNotPresent(inheritedMethods);
|
||||
methods.removeLessSpecifics();
|
||||
methods.compactAndTrim();
|
||||
res = methods.getArray();
|
||||
if (rd != null) {
|
||||
@ -2919,8 +2997,21 @@ public final class Class<T> implements java.io.Serializable,
|
||||
return (res == null ? res : getReflectionFactory().copyMethod(res));
|
||||
}
|
||||
|
||||
|
||||
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
|
||||
MethodArray interfaceCandidates = new MethodArray(2);
|
||||
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
|
||||
if (res != null)
|
||||
return res;
|
||||
|
||||
// Not found on class or superclass directly
|
||||
interfaceCandidates.removeLessSpecifics();
|
||||
return interfaceCandidates.getFirst(); // may be null
|
||||
}
|
||||
|
||||
private Method privateGetMethodRecursive(String name,
|
||||
Class<?>[] parameterTypes,
|
||||
boolean includeStaticMethods,
|
||||
MethodArray allInterfaceCandidates) {
|
||||
// Note: the intent is that the search algorithm this routine
|
||||
// uses be equivalent to the ordering imposed by
|
||||
// privateGetPublicMethods(). It fetches only the declared
|
||||
@ -2928,6 +3019,14 @@ public final class Class<T> implements java.io.Serializable,
|
||||
// number of Method objects which have to be created for the
|
||||
// common case where the method being requested is declared in
|
||||
// the class which is being queried.
|
||||
//
|
||||
// Due to default methods, unless a method is found on a superclass,
|
||||
// methods declared in any superinterface needs to be considered.
|
||||
// Collect all candidates declared in superinterfaces in {@code
|
||||
// allInterfaceCandidates} and select the most specific if no match on
|
||||
// a superclass is found.
|
||||
|
||||
// Must _not_ return root methods
|
||||
Method res;
|
||||
// Search declared public methods
|
||||
if ((res = searchMethods(privateGetDeclaredMethods(true),
|
||||
@ -2949,7 +3048,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
Class<?>[] interfaces = getInterfaces();
|
||||
for (Class<?> c : interfaces)
|
||||
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
|
||||
return res;
|
||||
allInterfaceCandidates.add(res);
|
||||
// Not found
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8029674
|
||||
* @summary Verify that the right interface methods are returned by
|
||||
* Class.getMethod() and Class.getMethods()
|
||||
* @run testng FilterNotMostSpecific
|
||||
*/
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class FilterNotMostSpecific {
|
||||
|
||||
@Test(dataProvider="getCases")
|
||||
public void testGetMethod(Class<?> iface) {
|
||||
boolean match = false;
|
||||
MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class);
|
||||
|
||||
for (MethodDesc expected : expectedMethods) {
|
||||
if (expected.isGetMethodReturn()) {
|
||||
try {
|
||||
Method m = iface.getMethod(expected.name());
|
||||
if (!assertMatch(expected, m))
|
||||
fail(failMsg(expected, m, iface));
|
||||
else
|
||||
match = true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
fail("expected: " + toMethodString(expected), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(match);
|
||||
}
|
||||
|
||||
@Test(dataProvider="getCases")
|
||||
public void testGetMethods(Class<?> iface) {
|
||||
List<Method> foundMethods = filterObjectMethods(iface.getMethods());
|
||||
MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class);
|
||||
Set<Method> used = new HashSet<>();
|
||||
|
||||
for (MethodDesc expected : expectedMethods) {
|
||||
boolean found = false;
|
||||
|
||||
for (Method m : foundMethods) {
|
||||
if (used.contains(m))
|
||||
continue;
|
||||
|
||||
if(expected.name().equals(m.getName()) &&
|
||||
expected.declaringClass() ==m.getDeclaringClass()) {
|
||||
|
||||
found = true;
|
||||
assertMatch(expected, m);
|
||||
used.add(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! found)
|
||||
fail("On: "+ iface +"\nDid not find " + toMethodString(expected) + " among " + foundMethods);
|
||||
}
|
||||
assertEquals(foundMethods.size(), expectedMethods.length,
|
||||
"\non: " + iface +
|
||||
"\nexpected: " + toMethodStrings(expectedMethods) +
|
||||
"\nfound: " + foundMethods + "\n");
|
||||
}
|
||||
|
||||
private boolean assertMatch(MethodDesc expected, Method m) {
|
||||
if (!expected.name().equals(m.getName()))
|
||||
return false;
|
||||
if (expected.declaringClass() != m.getDeclaringClass())
|
||||
return false;
|
||||
|
||||
if (expected.kind() == MethodKind.ABSTRACT)
|
||||
assertTrue(Modifier.isAbstract(m.getModifiers()), m + " should be ABSTRACT");
|
||||
else if (expected.kind() == MethodKind.CONCRETE)
|
||||
assertTrue(!Modifier.isAbstract(m.getModifiers()) && !m.isDefault(), m + " should be CONCRETE");
|
||||
else if (expected.kind() == MethodKind.DEFAULT)
|
||||
assertTrue(m.isDefault(), m + " should be DEFAULT");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String failMsg(MethodDesc expected, Method m, Class<?> iface) {
|
||||
return "\nOn interface: " + iface +
|
||||
"\nexpected: " + toMethodString(expected) +
|
||||
"\nfound: " + m;
|
||||
}
|
||||
|
||||
private static List<Method> filterObjectMethods(Method[] in) {
|
||||
return Arrays.stream(in).
|
||||
filter(m -> (m.getDeclaringClass() != java.lang.Object.class)).
|
||||
collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String toMethodString(MethodDesc m) {
|
||||
return m.declaringClass().getSimpleName().toString() + "." +
|
||||
m.name() + "()";
|
||||
}
|
||||
|
||||
private List<String> toMethodStrings(MethodDesc[] m) {
|
||||
return Arrays.stream(m).
|
||||
map(this::toMethodString)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(MethodDescs.class)
|
||||
public @interface MethodDesc {
|
||||
String name();
|
||||
Class<?> declaringClass();
|
||||
MethodKind kind() default MethodKind.ABSTRACT;
|
||||
boolean isGetMethodReturn() default false;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MethodDescs {
|
||||
MethodDesc[] value();
|
||||
}
|
||||
|
||||
public static enum MethodKind {
|
||||
ABSTRACT,
|
||||
CONCRETE,
|
||||
DEFAULT,
|
||||
}
|
||||
// base interfaces
|
||||
interface I { void nonDefault(); }
|
||||
interface J extends I { void nonDefault(); }
|
||||
|
||||
interface Jprim extends I {}
|
||||
interface Jbis extends Jprim { void nonDefault(); }
|
||||
|
||||
// interesting cases
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
||||
isGetMethodReturn=true)
|
||||
interface P1 extends Jbis {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
||||
interface P2 extends Jbis, Jprim {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
||||
interface P3 extends Jbis, Jprim, I {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
||||
interface P4 extends I, J {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
||||
interface P5 extends J, I {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||
isGetMethodReturn=true)
|
||||
interface K1 extends J {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=K1M.class,
|
||||
isGetMethodReturn=true)
|
||||
interface K1M extends J { void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
||||
interface K2 extends I, J {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
||||
interface K2O extends J, I {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=K2M.class,
|
||||
isGetMethodReturn=true)
|
||||
interface K2M extends J, I { void nonDefault(); }
|
||||
|
||||
// base interfaces default methods
|
||||
interface L { default void isDefault() {} void nonDefault(); }
|
||||
interface M extends L { default void isDefault() {} void nonDefault(); }
|
||||
|
||||
// test cases default methods
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface N1 extends M {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N1D.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N1D extends M { default void isDefault() {}}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=N1N.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface N1N extends M { void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N1DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N1DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N1DN extends M { default void isDefault() {} void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N2 extends M, L {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
interface N22 extends L, M {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2D.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N2D extends M, L { default void isDefault() {}}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2N.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N2N extends M, L { void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface N2DN extends M, L { default void isDefault() {} void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface O1 extends L, M, N2DN {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface O2 extends M, N2DN, L {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface O3 extends N2DN, L, M {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
abstract class C1 implements L, M, N2DN {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
abstract class C2 implements M, N2DN, L {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
abstract class C3 implements N2DN, L, M {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=C4.class,
|
||||
kind=MethodKind.CONCRETE, isGetMethodReturn=true)
|
||||
class C4 implements L, M, N2DN { public void nonDefault() {} }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=C5.class,
|
||||
kind=MethodKind.CONCRETE, isGetMethodReturn=true)
|
||||
class C5 implements M, N2DN, L { public void nonDefault() {} }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=C6.class,
|
||||
kind=MethodKind.CONCRETE, isGetMethodReturn=true)
|
||||
class C6 implements N2DN, L, M { public void nonDefault() {} }
|
||||
|
||||
// reabstraction
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R1 extends L, M, N2DN { void isDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R2 extends M, N2DN, L { void isDefault(); }
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface R3 extends N2DN, L, M { void isDefault(); }
|
||||
|
||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
||||
// default but not the most specific
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=R1.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R4 extends L, M, N2DN, R1 {}
|
||||
|
||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
||||
// default but not the most specific
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=R2.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R5 extends M, N2DN, R2, L {}
|
||||
|
||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
||||
// default but not the most specific
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=R3.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface R6 extends N2DN, R3, L, M {}
|
||||
|
||||
// the following three finds the "right" one
|
||||
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R7 extends L, M, R1, N2DN {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
||||
interface R8 extends M, R2, N2DN, L {}
|
||||
|
||||
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||
isGetMethodReturn=true)
|
||||
interface R9 extends R3, N2DN, L, M {}
|
||||
|
||||
// More reabstraction
|
||||
interface Z1 { void z(); }
|
||||
interface Z2 extends Z1 { default void z() {} }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
isGetMethodReturn=true, kind=MethodKind.DEFAULT)
|
||||
interface Z31 extends Z1, Z2 {}
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface Z32 extends Z2, Z1 {}
|
||||
|
||||
interface Z3 extends Z2, Z1 { void z(); }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
||||
interface Z41 extends Z1, Z2, Z3 { }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
||||
interface Z42 extends Z2, Z3, Z1 { }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface Z43 extends Z3, Z1, Z2 { }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
||||
abstract class ZC41 implements Z1, Z2, Z3 { }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
||||
abstract class ZC42 implements Z2, Z3, Z1 { }
|
||||
|
||||
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
abstract class ZC43 implements Z3, Z1, Z2 { }
|
||||
|
||||
// More reabstraction + concretization
|
||||
interface X1 { default void x() {} }
|
||||
interface X2 extends X1 { void x(); }
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X1.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
@MethodDesc(name="x", declaringClass=X2.class)
|
||||
interface X31 extends X1, X2 {}
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X2.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="x", declaringClass=X1.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface X32 extends X2, X1 {}
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface X3 extends X2, X1 { default void x() {} }
|
||||
|
||||
// order shouldn't matter here
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface X41 extends X1, X2, X3 { }
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface X42 extends X2, X3, X1 { }
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
interface X43 extends X3, X1, X2 { }
|
||||
|
||||
// order shouldn't matter here
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
abstract class XC41 implements X1, X2, X3 { }
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
abstract class XC42 implements X2, X3, X1 { }
|
||||
|
||||
@MethodDesc(name="x", declaringClass=X3.class,
|
||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||
abstract class XC43 implements X3, X1, X2 { }
|
||||
|
||||
interface K extends I, J { void nonDefault(); }
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
||||
abstract class ZZ1 implements I, J, K {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
||||
abstract class ZZ2 extends ZZ1 implements K, I, J {}
|
||||
|
||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
||||
abstract class ZZ3 extends ZZ2 implements J, K, I {}
|
||||
|
||||
// bridges
|
||||
interface B1A { Object m(); }
|
||||
interface B1B extends B1A { Map m(); }
|
||||
|
||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface B1C extends B1B { HashMap m(); }
|
||||
|
||||
@MethodDesc(name="m", declaringClass=B2.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=B2.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="m", declaringClass=B2.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface B2 extends B1C { HashMap m(); }
|
||||
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //HahsMap
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //Map
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //Object
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface B3A extends B2, B1A {}
|
||||
|
||||
// this one is funny since HashMap isn't a bridge thus not a default
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //HashMap
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //Map
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="m", declaringClass=B2.class, //Object
|
||||
kind=MethodKind.DEFAULT)
|
||||
@MethodDesc(name="m", declaringClass=B1C.class) //HashMap
|
||||
interface B3B extends B2, B1C {}
|
||||
|
||||
// same name different params type
|
||||
interface A1 { void m(); void m(int i); void m(int i, int j); }
|
||||
interface A2A extends A1 { void m(); void m(int i); void m(int i, int j); }
|
||||
interface A2B extends A1 { void m(); void m(int i); default void m(int i, int j) {} }
|
||||
|
||||
@MethodDesc(name="m", declaringClass=A1.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=A1.class)
|
||||
@MethodDesc(name="m", declaringClass=A1.class)
|
||||
@MethodDesc(name="m", declaringClass=A2A.class)
|
||||
@MethodDesc(name="m", declaringClass=A2A.class)
|
||||
@MethodDesc(name="m", declaringClass=A2A.class)
|
||||
interface A3A extends A1, A2A {}
|
||||
|
||||
@MethodDesc(name="m", declaringClass=A1.class,
|
||||
isGetMethodReturn=true)
|
||||
@MethodDesc(name="m", declaringClass=A1.class)
|
||||
@MethodDesc(name="m", declaringClass=A2B.class)
|
||||
@MethodDesc(name="m", declaringClass=A2B.class)
|
||||
@MethodDesc(name="m", declaringClass=A2B.class,
|
||||
kind=MethodKind.DEFAULT)
|
||||
interface A3B extends A1, A2B {}
|
||||
|
||||
@DataProvider
|
||||
public Object[][] getCases() { return CASES; }
|
||||
public static final Class<?>[][] CASES = {
|
||||
{ K1.class },
|
||||
{ K1M.class },
|
||||
{ K2.class },
|
||||
{ K2O.class },
|
||||
{ K2M.class },
|
||||
|
||||
{ N1.class },
|
||||
{ N1D.class },
|
||||
{ N1N.class },
|
||||
{ N1DN.class },
|
||||
|
||||
{ N2.class },
|
||||
{ N22.class },
|
||||
{ N2D.class },
|
||||
{ N2N.class },
|
||||
{ N2DN.class },
|
||||
|
||||
{ P1.class },
|
||||
{ P2.class },
|
||||
{ P3.class },
|
||||
{ P4.class },
|
||||
{ P5.class },
|
||||
|
||||
{ O1.class },
|
||||
{ O2.class },
|
||||
{ O3.class },
|
||||
|
||||
{ C1.class },
|
||||
{ C2.class },
|
||||
{ C3.class },
|
||||
|
||||
{ C4.class },
|
||||
{ C5.class },
|
||||
{ C6.class },
|
||||
|
||||
{ R1.class },
|
||||
{ R2.class },
|
||||
{ R3.class },
|
||||
|
||||
{ R4.class },
|
||||
{ R5.class },
|
||||
{ R6.class },
|
||||
|
||||
{ R7.class },
|
||||
{ R8.class },
|
||||
{ R9.class },
|
||||
|
||||
{ Z31.class },
|
||||
{ Z32.class },
|
||||
|
||||
{ Z41.class },
|
||||
{ Z42.class },
|
||||
{ Z43.class },
|
||||
|
||||
{ ZC41.class },
|
||||
{ ZC42.class },
|
||||
{ ZC43.class },
|
||||
|
||||
{ ZZ1.class },
|
||||
{ ZZ2.class },
|
||||
{ ZZ3.class },
|
||||
|
||||
{ X3.class },
|
||||
{ X31.class },
|
||||
{ X32.class },
|
||||
|
||||
{ X41.class },
|
||||
{ X42.class },
|
||||
{ X43.class },
|
||||
|
||||
{ XC41.class },
|
||||
{ XC42.class },
|
||||
{ XC43.class },
|
||||
|
||||
{ B1C.class },
|
||||
{ B2.class },
|
||||
{ B3A.class },
|
||||
{ B3B.class },
|
||||
|
||||
{ A3A.class },
|
||||
{ A3B.class },
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user