mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8172190: Re-apply the fix for bugs 8062389, 8029459, 8061950
Final fix for 8062389: Class.getMethod() is inconsistent with Class.getMethods() results, 8029459: getMethods returns methods that are not members of the class, 8061950: Class.getMethods() exhibits quadratic time complexity Reviewed-by: alanb, mchung, psandoz, dfuchs, darcy, redestad
This commit is contained in:
parent
94d019e9ba
commit
5ada754c71
12 changed files with 2553 additions and 520 deletions
|
@ -26,8 +26,6 @@
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.module.ModuleDescriptor.Version;
|
|
||||||
import java.lang.module.ModuleFinder;
|
|
||||||
import java.lang.module.ModuleReader;
|
import java.lang.module.ModuleReader;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -42,7 +40,6 @@ import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.GenericArrayType;
|
import java.lang.reflect.GenericArrayType;
|
||||||
import java.lang.reflect.GenericDeclaration;
|
import java.lang.reflect.GenericDeclaration;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Layer;
|
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
@ -54,7 +51,6 @@ import java.net.URL;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -62,19 +58,15 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||||
import jdk.internal.loader.BootLoader;
|
import jdk.internal.loader.BootLoader;
|
||||||
import jdk.internal.loader.BuiltinClassLoader;
|
import jdk.internal.loader.BuiltinClassLoader;
|
||||||
import jdk.internal.loader.ResourceHelper;
|
import jdk.internal.loader.ResourceHelper;
|
||||||
import jdk.internal.misc.SharedSecrets;
|
|
||||||
import jdk.internal.misc.Unsafe;
|
import jdk.internal.misc.Unsafe;
|
||||||
import jdk.internal.misc.VM;
|
import jdk.internal.misc.VM;
|
||||||
import jdk.internal.module.ModuleHashes;
|
|
||||||
import jdk.internal.reflect.CallerSensitive;
|
import jdk.internal.reflect.CallerSensitive;
|
||||||
import jdk.internal.reflect.ConstantPool;
|
import jdk.internal.reflect.ConstantPool;
|
||||||
import jdk.internal.reflect.Reflection;
|
import jdk.internal.reflect.Reflection;
|
||||||
|
@ -532,7 +524,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Class<?>[] empty = {};
|
Class<?>[] empty = {};
|
||||||
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
|
final Constructor<T> c = getReflectionFactory().copyConstructor(
|
||||||
|
getConstructor0(empty, Member.DECLARED));
|
||||||
// Disable accessibility checks on the constructor
|
// Disable accessibility checks on the constructor
|
||||||
// since we have to do the security check here anyway
|
// since we have to do the security check here anyway
|
||||||
// (the stack depth is wrong for the Constructor's
|
// (the stack depth is wrong for the Constructor's
|
||||||
|
@ -1024,6 +1017,11 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* @return an array of interfaces directly implemented by this class
|
* @return an array of interfaces directly implemented by this class
|
||||||
*/
|
*/
|
||||||
public Class<?>[] getInterfaces() {
|
public Class<?>[] getInterfaces() {
|
||||||
|
// defensively copy before handing over to user code
|
||||||
|
return getInterfaces(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?>[] getInterfaces(boolean cloneArray) {
|
||||||
ReflectionData<T> rd = reflectionData();
|
ReflectionData<T> rd = reflectionData();
|
||||||
if (rd == null) {
|
if (rd == null) {
|
||||||
// no cloning required
|
// no cloning required
|
||||||
|
@ -1034,8 +1032,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
interfaces = getInterfaces0();
|
interfaces = getInterfaces0();
|
||||||
rd.interfaces = interfaces;
|
rd.interfaces = interfaces;
|
||||||
}
|
}
|
||||||
// defensively copy before handing over to user code
|
// defensively copy if requested
|
||||||
return interfaces.clone();
|
return cloneArray ? interfaces.clone() : interfaces;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1767,15 +1765,6 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* Class} object, including those declared by the class or interface and
|
* Class} object, including those declared by the class or interface and
|
||||||
* those inherited from superclasses and superinterfaces.
|
* those inherited from superclasses and superinterfaces.
|
||||||
*
|
*
|
||||||
* <p> If this {@code Class} object represents a type that has multiple
|
|
||||||
* public methods with the same name and parameter types, but different
|
|
||||||
* return types, then the returned array has a {@code Method} object for
|
|
||||||
* each such method.
|
|
||||||
*
|
|
||||||
* <p> If this {@code Class} object represents a type with a class
|
|
||||||
* initialization method {@code <clinit>}, then the returned array does
|
|
||||||
* <em>not</em> have a corresponding {@code Method} object.
|
|
||||||
*
|
|
||||||
* <p> If this {@code Class} object represents an array type, then the
|
* <p> If this {@code Class} object represents an array type, then the
|
||||||
* returned array has a {@code Method} object for each of the public
|
* returned array has a {@code Method} object for each of the public
|
||||||
* methods inherited by the array type from {@code Object}. It does not
|
* methods inherited by the array type from {@code Object}. It does not
|
||||||
|
@ -1788,16 +1777,54 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* has length 0. (Note that a {@code Class} object which represents a class
|
* has length 0. (Note that a {@code Class} object which represents a class
|
||||||
* always has public methods, inherited from {@code Object}.)
|
* always has public methods, inherited from {@code Object}.)
|
||||||
*
|
*
|
||||||
* <p> If this {@code Class} object represents a primitive type or void,
|
* <p> The returned array never contains methods with names "{@code <init>}"
|
||||||
* then the returned array has length 0.
|
* or "{@code <clinit>}".
|
||||||
*
|
|
||||||
* <p> Static methods declared in superinterfaces of the class or interface
|
|
||||||
* represented by this {@code Class} object are not considered members of
|
|
||||||
* the class or interface.
|
|
||||||
*
|
*
|
||||||
* <p> The elements in the returned array are not sorted and are not in any
|
* <p> The elements in the returned array are not sorted and are not in any
|
||||||
* particular order.
|
* particular order.
|
||||||
*
|
*
|
||||||
|
* <p> Generally, the result is computed as with the following 4 step algorithm.
|
||||||
|
* Let C be the class or interface represented by this {@code Class} object:
|
||||||
|
* <ol>
|
||||||
|
* <li> A union of methods is composed of:
|
||||||
|
* <ol type="a">
|
||||||
|
* <li> C's declared public instance and static methods as returned by
|
||||||
|
* {@link #getDeclaredMethods()} and filtered to include only public
|
||||||
|
* methods.</li>
|
||||||
|
* <li> If C is a class other than {@code Object}, then include the result
|
||||||
|
* of invoking this algorithm recursively on the superclass of C.</li>
|
||||||
|
* <li> Include the results of invoking this algorithm recursively on all
|
||||||
|
* direct superinterfaces of C, but include only instance methods.</li>
|
||||||
|
* </ol></li>
|
||||||
|
* <li> Union from step 1 is partitioned into subsets of methods with same
|
||||||
|
* signature (name, parameter types) and return type.</li>
|
||||||
|
* <li> Within each such subset only the most specific methods are selected.
|
||||||
|
* Let method M be a method from a set of methods with same signature
|
||||||
|
* and return type. M is most specific if there is no such method
|
||||||
|
* N != M from the same set, such that N is more specific than M.
|
||||||
|
* N is more specific than M if:
|
||||||
|
* <ol type="a">
|
||||||
|
* <li> N is declared by a class and M is declared by an interface; or</li>
|
||||||
|
* <li> N and M are both declared by classes or both by interfaces and
|
||||||
|
* N's declaring type is the same as or a subtype of M's declaring type
|
||||||
|
* (clearly, if M's and N's declaring types are the same type, then
|
||||||
|
* M and N are the same method).</li>
|
||||||
|
* </ol></li>
|
||||||
|
* <li> The result of this algorithm is the union of all selected methods from
|
||||||
|
* step 3.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @apiNote There may be more than one method with a particular name
|
||||||
|
* and parameter types in a class because while the Java language forbids a
|
||||||
|
* class to declare multiple methods with the same signature but different
|
||||||
|
* return types, the Java virtual machine does not. This
|
||||||
|
* increased flexibility in the virtual machine can be used to
|
||||||
|
* implement various language features. For example, covariant
|
||||||
|
* returns can be implemented with {@linkplain
|
||||||
|
* java.lang.reflect.Method#isBridge bridge methods}; the bridge
|
||||||
|
* method and the overriding method would have the same
|
||||||
|
* signature but different return types.
|
||||||
|
*
|
||||||
* @return the array of {@code Method} objects representing the
|
* @return the array of {@code Method} objects representing the
|
||||||
* public methods of this class
|
* public methods of this class
|
||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
|
@ -1900,12 +1927,13 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
@CallerSensitive
|
@CallerSensitive
|
||||||
public Field getField(String name)
|
public Field getField(String name)
|
||||||
throws NoSuchFieldException, SecurityException {
|
throws NoSuchFieldException, SecurityException {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
||||||
Field field = getField0(name);
|
Field field = getField0(name);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(name);
|
||||||
}
|
}
|
||||||
return field;
|
return getReflectionFactory().copyField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1919,47 +1947,69 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* order. If {@code parameterTypes} is {@code null}, it is
|
* order. If {@code parameterTypes} is {@code null}, it is
|
||||||
* treated as if it were an empty array.
|
* treated as if it were an empty array.
|
||||||
*
|
*
|
||||||
* <p> If the {@code name} is "{@code <init>}" or "{@code <clinit>}" a
|
* <p> If this {@code Class} object represents an array type, then this
|
||||||
* {@code NoSuchMethodException} is raised. Otherwise, the method to
|
* method finds any public method inherited by the array type from
|
||||||
* be reflected is determined by the algorithm that follows. Let C be the
|
* {@code Object} except method {@code clone()}.
|
||||||
* class or interface represented by this object:
|
|
||||||
* <OL>
|
|
||||||
* <LI> C is searched for a <I>matching method</I>, as defined below. If a
|
|
||||||
* matching method is found, it is reflected.</LI>
|
|
||||||
* <LI> If no matching method is found by step 1 then:
|
|
||||||
* <OL TYPE="a">
|
|
||||||
* <LI> If C is a class other than {@code Object}, then this algorithm is
|
|
||||||
* invoked recursively on the superclass of C.</LI>
|
|
||||||
* <LI> If C is the class {@code Object}, or if C is an interface, then
|
|
||||||
* the superinterfaces of C (if any) are searched for a matching
|
|
||||||
* method. If any such method is found, it is reflected.</LI>
|
|
||||||
* </OL></LI>
|
|
||||||
* </OL>
|
|
||||||
*
|
*
|
||||||
* <p> To find a matching method in a class or interface C: If C
|
* <p> If this {@code Class} object represents an interface then this
|
||||||
* declares exactly one public method with the specified name and exactly
|
* method does not find any implicitly declared method from
|
||||||
* the same formal parameter types, that is the method reflected. If more
|
* {@code Object}. Therefore, if no methods are explicitly declared in
|
||||||
* than one such method is found in C, and one of these methods has a
|
* this interface or any of its superinterfaces, then this method does not
|
||||||
* return type that is more specific than any of the others, that method is
|
* find any method.
|
||||||
* reflected; otherwise one of the methods is chosen arbitrarily.
|
|
||||||
*
|
*
|
||||||
* <p>Note that there may be more than one matching method in a
|
* <p> This method does not find any method with name "{@code <init>}" or
|
||||||
* class because while the Java language forbids a class to
|
* "{@code <clinit>}".
|
||||||
* declare multiple methods with the same signature but different
|
*
|
||||||
|
* <p> Generally, the method to be reflected is determined by the 4 step
|
||||||
|
* algorithm that follows.
|
||||||
|
* Let C be the class or interface represented by this {@code Class} object:
|
||||||
|
* <ol>
|
||||||
|
* <li> A union of methods is composed of:
|
||||||
|
* <ol type="a">
|
||||||
|
* <li> C's declared public instance and static methods as returned by
|
||||||
|
* {@link #getDeclaredMethods()} and filtered to include only public
|
||||||
|
* methods that match given {@code name} and {@code parameterTypes}</li>
|
||||||
|
* <li> If C is a class other than {@code Object}, then include the result
|
||||||
|
* of invoking this algorithm recursively on the superclass of C.</li>
|
||||||
|
* <li> Include the results of invoking this algorithm recursively on all
|
||||||
|
* direct superinterfaces of C, but include only instance methods.</li>
|
||||||
|
* </ol></li>
|
||||||
|
* <li> This union is partitioned into subsets of methods with same
|
||||||
|
* return type (the selection of methods from step 1 also guarantees that
|
||||||
|
* they have the same method name and parameter types).</li>
|
||||||
|
* <li> Within each such subset only the most specific methods are selected.
|
||||||
|
* Let method M be a method from a set of methods with same VM
|
||||||
|
* signature (return type, name, parameter types).
|
||||||
|
* M is most specific if there is no such method N != M from the same
|
||||||
|
* set, such that N is more specific than M. N is more specific than M
|
||||||
|
* if:
|
||||||
|
* <ol type="a">
|
||||||
|
* <li> N is declared by a class and M is declared by an interface; or</li>
|
||||||
|
* <li> N and M are both declared by classes or both by interfaces and
|
||||||
|
* N's declaring type is the same as or a subtype of M's declaring type
|
||||||
|
* (clearly, if M's and N's declaring types are the same type, then
|
||||||
|
* M and N are the same method).</li>
|
||||||
|
* </ol></li>
|
||||||
|
* <li> The result of this algorithm is chosen arbitrarily from the methods
|
||||||
|
* with most specific return type among all selected methods from step 3.
|
||||||
|
* Let R be a return type of a method M from the set of all selected methods
|
||||||
|
* from step 3. M is a method with most specific return type if there is
|
||||||
|
* no such method N != M from the same set, having return type S != R,
|
||||||
|
* such that S is a subtype of R as determined by
|
||||||
|
* R.class.{@link #isAssignableFrom}(S.class).
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @apiNote There may be more than one method with matching name and
|
||||||
|
* parameter types in a class because while the Java language forbids a
|
||||||
|
* class to declare multiple methods with the same signature but different
|
||||||
* return types, the Java virtual machine does not. This
|
* return types, the Java virtual machine does not. This
|
||||||
* increased flexibility in the virtual machine can be used to
|
* increased flexibility in the virtual machine can be used to
|
||||||
* implement various language features. For example, covariant
|
* implement various language features. For example, covariant
|
||||||
* returns can be implemented with {@linkplain
|
* returns can be implemented with {@linkplain
|
||||||
* java.lang.reflect.Method#isBridge bridge methods}; the bridge
|
* java.lang.reflect.Method#isBridge bridge methods}; the bridge
|
||||||
* method and the method being overridden would have the same
|
* method and the overriding method would have the same
|
||||||
* signature but different return types.
|
* signature but different return types. This method would return the
|
||||||
*
|
* overriding method as it would have a more specific return type.
|
||||||
* <p> If this {@code Class} object represents an array type, then this
|
|
||||||
* method does not find the {@code clone()} method.
|
|
||||||
*
|
|
||||||
* <p> Static methods declared in superinterfaces of the class or interface
|
|
||||||
* represented by this {@code Class} object are not considered members of
|
|
||||||
* the class or interface.
|
|
||||||
*
|
*
|
||||||
* @param name the name of the method
|
* @param name the name of the method
|
||||||
* @param parameterTypes the list of parameters
|
* @param parameterTypes the list of parameters
|
||||||
|
@ -1983,12 +2033,13 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
@CallerSensitive
|
@CallerSensitive
|
||||||
public Method getMethod(String name, Class<?>... parameterTypes)
|
public Method getMethod(String name, Class<?>... parameterTypes)
|
||||||
throws NoSuchMethodException, SecurityException {
|
throws NoSuchMethodException, SecurityException {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
||||||
Method method = getMethod0(name, parameterTypes, true);
|
Method method = getMethod0(name, parameterTypes);
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
|
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
|
||||||
}
|
}
|
||||||
return method;
|
return getReflectionFactory().copyMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2004,7 +2055,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
* "<init>"or "<clinit>".
|
* "<init>"or "<clinit>".
|
||||||
*/
|
*/
|
||||||
Method getMethodOrNull(String name, Class<?>... parameterTypes) {
|
Method getMethodOrNull(String name, Class<?>... parameterTypes) {
|
||||||
return getMethod0(name, parameterTypes, true);
|
Objects.requireNonNull(name);
|
||||||
|
Method method = getMethod0(name, parameterTypes);
|
||||||
|
return method == null ? null : getReflectionFactory().copyMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2041,7 +2094,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
public Constructor<T> getConstructor(Class<?>... parameterTypes)
|
public Constructor<T> getConstructor(Class<?>... parameterTypes)
|
||||||
throws NoSuchMethodException, SecurityException {
|
throws NoSuchMethodException, SecurityException {
|
||||||
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
|
||||||
return getConstructor0(parameterTypes, Member.PUBLIC);
|
return getReflectionFactory().copyConstructor(
|
||||||
|
getConstructor0(parameterTypes, Member.PUBLIC));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2283,12 +2337,13 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
@CallerSensitive
|
@CallerSensitive
|
||||||
public Field getDeclaredField(String name)
|
public Field getDeclaredField(String name)
|
||||||
throws NoSuchFieldException, SecurityException {
|
throws NoSuchFieldException, SecurityException {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
||||||
Field field = searchFields(privateGetDeclaredFields(false), name);
|
Field field = searchFields(privateGetDeclaredFields(false), name);
|
||||||
if (field == null) {
|
if (field == null) {
|
||||||
throw new NoSuchFieldException(name);
|
throw new NoSuchFieldException(name);
|
||||||
}
|
}
|
||||||
return field;
|
return getReflectionFactory().copyField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2343,12 +2398,13 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
@CallerSensitive
|
@CallerSensitive
|
||||||
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
|
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
|
||||||
throws NoSuchMethodException, SecurityException {
|
throws NoSuchMethodException, SecurityException {
|
||||||
|
Objects.requireNonNull(name);
|
||||||
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
||||||
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
|
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
|
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
|
||||||
}
|
}
|
||||||
return method;
|
return getReflectionFactory().copyMethod(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2394,7 +2450,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
|
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
|
||||||
throws NoSuchMethodException, SecurityException {
|
throws NoSuchMethodException, SecurityException {
|
||||||
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
|
||||||
return getConstructor0(parameterTypes, Member.DECLARED);
|
return getReflectionFactory().copyConstructor(
|
||||||
|
getConstructor0(parameterTypes, Member.DECLARED));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3004,180 +3061,6 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
|
||||||
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) {
|
|
||||||
if (length == methods.length) {
|
|
||||||
methods = Arrays.copyOf(methods, 2 * methods.length);
|
|
||||||
}
|
|
||||||
methods[length++] = m;
|
|
||||||
|
|
||||||
if (m != null && m.isDefault())
|
|
||||||
defaults++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAll(Method[] ma) {
|
|
||||||
for (Method m : ma) {
|
|
||||||
add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAll(MethodArray ma) {
|
|
||||||
for (int i = 0; i < ma.length(); i++) {
|
|
||||||
add(ma.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addIfNotPresent(Method newMethod) {
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
Method m = methods[i];
|
|
||||||
if (m == newMethod || (m != null && m.equals(newMethod))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add(newMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAllIfNotPresent(MethodArray newMethods) {
|
|
||||||
for (int i = 0; i < newMethods.length(); i++) {
|
|
||||||
Method m = newMethods.get(i);
|
|
||||||
if (m != null) {
|
|
||||||
addIfNotPresent(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length() {
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Method get(int i) {
|
|
||||||
return methods[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 && 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
|
|
||||||
for (int pos = 0; pos < length; pos++) {
|
|
||||||
Method m = methods[pos];
|
|
||||||
if (m != null) {
|
|
||||||
if (pos != newPos) {
|
|
||||||
methods[newPos] = m;
|
|
||||||
}
|
|
||||||
newPos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newPos != methods.length) {
|
|
||||||
methods = Arrays.copyOf(methods, newPos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns an array of "root" methods. These Method objects must NOT
|
// Returns an array of "root" methods. These Method objects must NOT
|
||||||
// be propagated to the outside world, but must instead be copied
|
// be propagated to the outside world, but must instead be copied
|
||||||
// via ReflectionFactory.copyMethod.
|
// via ReflectionFactory.copyMethod.
|
||||||
|
@ -3190,51 +3073,29 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
}
|
}
|
||||||
|
|
||||||
// No cached value available; compute value recursively.
|
// No cached value available; compute value recursively.
|
||||||
// Start by fetching public declared methods
|
// Start by fetching public declared methods...
|
||||||
MethodArray methods = new MethodArray();
|
PublicMethods pms = new PublicMethods();
|
||||||
{
|
for (Method m : privateGetDeclaredMethods(/* publicOnly */ true)) {
|
||||||
Method[] tmp = privateGetDeclaredMethods(true);
|
pms.merge(m);
|
||||||
methods.addAll(tmp);
|
|
||||||
}
|
}
|
||||||
// Now recur over superclass and direct superinterfaces.
|
// ...then recur over superclass methods...
|
||||||
// Go over superinterfaces first so we can more easily filter
|
Class<?> sc = getSuperclass();
|
||||||
// out concrete implementations inherited from superclasses at
|
if (sc != null) {
|
||||||
// the end.
|
for (Method m : sc.privateGetPublicMethods()) {
|
||||||
MethodArray inheritedMethods = new MethodArray();
|
pms.merge(m);
|
||||||
for (Class<?> i : getInterfaces()) {
|
|
||||||
inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods());
|
|
||||||
}
|
|
||||||
if (!isInterface()) {
|
|
||||||
Class<?> c = getSuperclass();
|
|
||||||
if (c != null) {
|
|
||||||
MethodArray supers = new MethodArray();
|
|
||||||
supers.addAll(c.privateGetPublicMethods());
|
|
||||||
// Filter out concrete implementations of any
|
|
||||||
// interface methods
|
|
||||||
for (int i = 0; i < supers.length(); i++) {
|
|
||||||
Method m = supers.get(i);
|
|
||||||
if (m != null &&
|
|
||||||
!Modifier.isAbstract(m.getModifiers()) &&
|
|
||||||
!m.isDefault()) {
|
|
||||||
inheritedMethods.removeByNameAndDescriptor(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Insert superclass's inherited methods before
|
|
||||||
// superinterfaces' to satisfy getMethod's search
|
|
||||||
// order
|
|
||||||
supers.addAll(inheritedMethods);
|
|
||||||
inheritedMethods = supers;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Filter out all local methods from inherited ones
|
// ...and finally over direct superinterfaces.
|
||||||
for (int i = 0; i < methods.length(); i++) {
|
for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {
|
||||||
Method m = methods.get(i);
|
for (Method m : intf.privateGetPublicMethods()) {
|
||||||
inheritedMethods.removeByNameAndDescriptor(m);
|
// static interface methods are not inherited
|
||||||
|
if (!Modifier.isStatic(m.getModifiers())) {
|
||||||
|
pms.merge(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
methods.addAllIfNotPresent(inheritedMethods);
|
|
||||||
methods.removeLessSpecifics();
|
res = pms.toArray();
|
||||||
methods.compactAndTrim();
|
|
||||||
res = methods.getArray();
|
|
||||||
if (rd != null) {
|
if (rd != null) {
|
||||||
rd.publicMethods = res;
|
rd.publicMethods = res;
|
||||||
}
|
}
|
||||||
|
@ -3246,17 +3107,20 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
// Helpers for fetchers of one field, method, or constructor
|
// Helpers for fetchers of one field, method, or constructor
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// This method does not copy the returned Field object!
|
||||||
private static Field searchFields(Field[] fields, String name) {
|
private static Field searchFields(Field[] fields, String name) {
|
||||||
String internedName = name.intern();
|
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
if (field.getName() == internedName) {
|
if (field.getName().equals(name)) {
|
||||||
return getReflectionFactory().copyField(field);
|
return field;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Field getField0(String name) throws NoSuchFieldException {
|
// Returns a "root" Field object. This Field object must NOT
|
||||||
|
// be propagated to the outside world, but must instead be copied
|
||||||
|
// via ReflectionFactory.copyField.
|
||||||
|
private Field getField0(String name) {
|
||||||
// Note: the intent is that the search algorithm this routine
|
// Note: the intent is that the search algorithm this routine
|
||||||
// uses be equivalent to the ordering imposed by
|
// uses be equivalent to the ordering imposed by
|
||||||
// privateGetPublicFields(). It fetches only the declared
|
// privateGetPublicFields(). It fetches only the declared
|
||||||
|
@ -3270,7 +3134,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
// Direct superinterfaces, recursively
|
// Direct superinterfaces, recursively
|
||||||
Class<?>[] interfaces = getInterfaces();
|
Class<?>[] interfaces = getInterfaces(/* cloneArray */ false);
|
||||||
for (Class<?> c : interfaces) {
|
for (Class<?> c : interfaces) {
|
||||||
if ((res = c.getField0(name)) != null) {
|
if ((res = c.getField0(name)) != null) {
|
||||||
return res;
|
return res;
|
||||||
|
@ -3288,87 +3152,85 @@ public final class Class<T> implements java.io.Serializable,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This method does not copy the returned Method object!
|
||||||
private static Method searchMethods(Method[] methods,
|
private static Method searchMethods(Method[] methods,
|
||||||
String name,
|
String name,
|
||||||
Class<?>[] parameterTypes)
|
Class<?>[] parameterTypes)
|
||||||
{
|
{
|
||||||
|
ReflectionFactory fact = getReflectionFactory();
|
||||||
Method res = null;
|
Method res = null;
|
||||||
String internedName = name.intern();
|
|
||||||
for (Method m : methods) {
|
for (Method m : methods) {
|
||||||
if (m.getName() == internedName
|
if (m.getName().equals(name)
|
||||||
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
|
&& arrayContentsEq(parameterTypes,
|
||||||
|
fact.getExecutableSharedParameterTypes(m))
|
||||||
&& (res == null
|
&& (res == null
|
||||||
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
|
|| (res.getReturnType() != m.getReturnType()
|
||||||
|
&& res.getReturnType().isAssignableFrom(m.getReturnType()))))
|
||||||
res = m;
|
res = m;
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
return (res == null ? res : getReflectionFactory().copyMethod(res));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
|
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||||
MethodArray interfaceCandidates = new MethodArray(2);
|
|
||||||
Method res = privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
|
// Returns a "root" Method object. This Method object must NOT
|
||||||
if (res != null)
|
// be propagated to the outside world, but must instead be copied
|
||||||
|
// via ReflectionFactory.copyMethod.
|
||||||
|
private Method getMethod0(String name, Class<?>[] parameterTypes) {
|
||||||
|
PublicMethods.MethodList res = getMethodsRecursive(
|
||||||
|
name,
|
||||||
|
parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes,
|
||||||
|
/* includeStatic */ true);
|
||||||
|
return res == null ? null : res.getMostSpecific();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a list of "root" Method objects. These Method objects must NOT
|
||||||
|
// be propagated to the outside world, but must instead be copied
|
||||||
|
// via ReflectionFactory.copyMethod.
|
||||||
|
private PublicMethods.MethodList getMethodsRecursive(String name,
|
||||||
|
Class<?>[] parameterTypes,
|
||||||
|
boolean includeStatic) {
|
||||||
|
// 1st check declared public methods
|
||||||
|
Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true);
|
||||||
|
PublicMethods.MethodList res = PublicMethods.MethodList
|
||||||
|
.filter(methods, name, parameterTypes, includeStatic);
|
||||||
|
// if there is at least one match among declared methods, we need not
|
||||||
|
// search any further as such match surely overrides matching methods
|
||||||
|
// declared in superclass(es) or interface(s).
|
||||||
|
if (res != null) {
|
||||||
return res;
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// Not found on class or superclass directly
|
// if there was no match among declared methods,
|
||||||
interfaceCandidates.removeLessSpecifics();
|
// we must consult the superclass (if any) recursively...
|
||||||
return interfaceCandidates.getFirst(); // may be null
|
Class<?> sc = getSuperclass();
|
||||||
}
|
if (sc != null) {
|
||||||
|
res = sc.getMethodsRecursive(name, parameterTypes, includeStatic);
|
||||||
private Method privateGetMethodRecursive(String name,
|
}
|
||||||
Class<?>[] parameterTypes,
|
|
||||||
boolean includeStaticMethods,
|
// ...and coalesce the superclass methods with methods obtained
|
||||||
MethodArray allInterfaceCandidates) {
|
// from directly implemented interfaces excluding static methods...
|
||||||
// Note: the intent is that the search algorithm this routine
|
for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {
|
||||||
// uses be equivalent to the ordering imposed by
|
res = PublicMethods.MethodList.merge(
|
||||||
// privateGetPublicMethods(). It fetches only the declared
|
res, intf.getMethodsRecursive(name, parameterTypes,
|
||||||
// public methods for each class, however, to reduce the
|
/* includeStatic */ false));
|
||||||
// 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.
|
return res;
|
||||||
//
|
|
||||||
// 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),
|
|
||||||
name,
|
|
||||||
parameterTypes)) != null) {
|
|
||||||
if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
// Search superclass's methods
|
|
||||||
if (!isInterface()) {
|
|
||||||
Class<? super T> c = getSuperclass();
|
|
||||||
if (c != null) {
|
|
||||||
if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Search superinterfaces' methods
|
|
||||||
Class<?>[] interfaces = getInterfaces();
|
|
||||||
for (Class<?> c : interfaces)
|
|
||||||
if ((res = c.getMethod0(name, parameterTypes, false)) != null)
|
|
||||||
allInterfaceCandidates.add(res);
|
|
||||||
// Not found
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a "root" Constructor object. This Constructor object must NOT
|
||||||
|
// be propagated to the outside world, but must instead be copied
|
||||||
|
// via ReflectionFactory.copyConstructor.
|
||||||
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
|
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
|
||||||
int which) throws NoSuchMethodException
|
int which) throws NoSuchMethodException
|
||||||
{
|
{
|
||||||
|
ReflectionFactory fact = getReflectionFactory();
|
||||||
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
|
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
|
||||||
for (Constructor<T> constructor : constructors) {
|
for (Constructor<T> constructor : constructors) {
|
||||||
if (arrayContentsEq(parameterTypes,
|
if (arrayContentsEq(parameterTypes,
|
||||||
constructor.getParameterTypes())) {
|
fact.getExecutableSharedParameterTypes(constructor))) {
|
||||||
return getReflectionFactory().copyConstructor(constructor);
|
return constructor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
|
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
|
||||||
|
|
272
jdk/src/java.base/share/classes/java/lang/PublicMethods.java
Normal file
272
jdk/src/java.base/share/classes/java/lang/PublicMethods.java
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
package java.lang;
|
||||||
|
|
||||||
|
import jdk.internal.reflect.ReflectionFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of most specific public methods. Methods are added to it using
|
||||||
|
* {@link #merge(Method)} method. Only the most specific methods for a
|
||||||
|
* particular signature are kept.
|
||||||
|
*/
|
||||||
|
final class PublicMethods {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a map of (method name, parameter types) -> linked list of Method(s)
|
||||||
|
*/
|
||||||
|
private final Map<Key, MethodList> map = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* keeps track of the number of collected methods
|
||||||
|
*/
|
||||||
|
private int methodCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges new method with existing methods. New method is either
|
||||||
|
* ignored (if a more specific method with same signature exists) or added
|
||||||
|
* to the collection. When it is added to the collection, it may replace one
|
||||||
|
* or more existing methods with same signature if they are less specific
|
||||||
|
* than added method.
|
||||||
|
* See comments in code...
|
||||||
|
*/
|
||||||
|
void merge(Method method) {
|
||||||
|
Key key = new Key(method);
|
||||||
|
MethodList existing = map.get(key);
|
||||||
|
int xLen = existing == null ? 0 : existing.length();
|
||||||
|
MethodList merged = MethodList.merge(existing, method);
|
||||||
|
methodCount += merged.length() - xLen;
|
||||||
|
// replace if head of list changed
|
||||||
|
if (merged != existing) {
|
||||||
|
map.put(key, merged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps methods to array.
|
||||||
|
*/
|
||||||
|
Method[] toArray() {
|
||||||
|
Method[] array = new Method[methodCount];
|
||||||
|
int i = 0;
|
||||||
|
for (MethodList ml : map.values()) {
|
||||||
|
for (; ml != null; ml = ml.next) {
|
||||||
|
array[i++] = ml.method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method (name, parameter types) tuple.
|
||||||
|
*/
|
||||||
|
private static final class Key {
|
||||||
|
private static final ReflectionFactory reflectionFactory =
|
||||||
|
AccessController.doPrivileged(
|
||||||
|
new ReflectionFactory.GetReflectionFactoryAction());
|
||||||
|
|
||||||
|
private final String name; // must be interned (as from Method.getName())
|
||||||
|
private final Class<?>[] ptypes;
|
||||||
|
|
||||||
|
Key(Method method) {
|
||||||
|
name = method.getName();
|
||||||
|
ptypes = reflectionFactory.getExecutableSharedParameterTypes(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean matches(Method method,
|
||||||
|
String name, // may not be interned
|
||||||
|
Class<?>[] ptypes) {
|
||||||
|
return method.getName().equals(name) &&
|
||||||
|
Arrays.equals(
|
||||||
|
reflectionFactory.getExecutableSharedParameterTypes(method),
|
||||||
|
ptypes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof Key)) return false;
|
||||||
|
Key that = (Key) o;
|
||||||
|
//noinspection StringEquality (guaranteed interned String(s))
|
||||||
|
return name == that.name &&
|
||||||
|
Arrays.equals(ptypes, that.ptypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return System.identityHashCode(name) + // guaranteed interned String
|
||||||
|
31 * Arrays.hashCode(ptypes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node of a inked list containing Method(s) sharing the same
|
||||||
|
* (name, parameter types) tuple.
|
||||||
|
*/
|
||||||
|
static final class MethodList {
|
||||||
|
Method method;
|
||||||
|
MethodList next;
|
||||||
|
|
||||||
|
private MethodList(Method method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the head of a linked list containing given {@code methods}
|
||||||
|
* filtered by given method {@code name}, parameter types
|
||||||
|
* {@code ptypes} and including or excluding static methods as
|
||||||
|
* requested by {@code includeStatic} flag.
|
||||||
|
*/
|
||||||
|
static MethodList filter(Method[] methods, String name,
|
||||||
|
Class<?>[] ptypes, boolean includeStatic) {
|
||||||
|
MethodList head = null, tail = null;
|
||||||
|
for (Method method : methods) {
|
||||||
|
if ((includeStatic || !Modifier.isStatic(method.getModifiers())) &&
|
||||||
|
Key.matches(method, name, ptypes)) {
|
||||||
|
if (tail == null) {
|
||||||
|
head = tail = new MethodList(method);
|
||||||
|
} else {
|
||||||
|
tail = tail.next = new MethodList(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should only be called with the {@code head} (possibly null)
|
||||||
|
* of a list of Method(s) that share the same (method name, parameter types)
|
||||||
|
* and another {@code methodList} that also contains Method(s) with the
|
||||||
|
* same and equal (method name, parameter types) as the 1st list.
|
||||||
|
* It modifies the 1st list and returns the head of merged list
|
||||||
|
* containing only the most specific methods for each signature
|
||||||
|
* (i.e. return type). The returned head of the merged list may or
|
||||||
|
* may not be the same as the {@code head} of the given list.
|
||||||
|
* The given {@code methodList} is not modified.
|
||||||
|
*/
|
||||||
|
static MethodList merge(MethodList head, MethodList methodList) {
|
||||||
|
for (MethodList ml = methodList; ml != null; ml = ml.next) {
|
||||||
|
head = merge(head, ml.method);
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodList merge(MethodList head, Method method) {
|
||||||
|
Class<?> dclass = method.getDeclaringClass();
|
||||||
|
Class<?> rtype = method.getReturnType();
|
||||||
|
MethodList prev = null;
|
||||||
|
for (MethodList l = head; l != null; l = l.next) {
|
||||||
|
// eXisting method
|
||||||
|
Method xmethod = l.method;
|
||||||
|
// only merge methods with same signature:
|
||||||
|
// (return type, name, parameter types) tuple
|
||||||
|
// as we only keep methods with same (name, parameter types)
|
||||||
|
// tuple together in one list, we only need to check return type
|
||||||
|
if (rtype == xmethod.getReturnType()) {
|
||||||
|
Class<?> xdclass = xmethod.getDeclaringClass();
|
||||||
|
if (dclass.isInterface() == xdclass.isInterface()) {
|
||||||
|
// both methods are declared by interfaces
|
||||||
|
// or both by classes
|
||||||
|
if (dclass.isAssignableFrom(xdclass)) {
|
||||||
|
// existing method is the same or overrides
|
||||||
|
// new method - ignore new method
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
if (xdclass.isAssignableFrom(dclass)) {
|
||||||
|
// new method overrides existing
|
||||||
|
// method - knock out existing method
|
||||||
|
if (prev != null) {
|
||||||
|
prev.next = l.next;
|
||||||
|
} else {
|
||||||
|
head = l.next;
|
||||||
|
}
|
||||||
|
// keep iterating
|
||||||
|
} else {
|
||||||
|
// unrelated (should only happen for interfaces)
|
||||||
|
prev = l;
|
||||||
|
// keep iterating
|
||||||
|
}
|
||||||
|
} else if (dclass.isInterface()) {
|
||||||
|
// new method is declared by interface while
|
||||||
|
// existing method is declared by class -
|
||||||
|
// ignore new method
|
||||||
|
return head;
|
||||||
|
} else /* xdclass.isInterface() */ {
|
||||||
|
// new method is declared by class while
|
||||||
|
// existing method is declared by interface -
|
||||||
|
// knock out existing method
|
||||||
|
if (prev != null) {
|
||||||
|
prev.next = l.next;
|
||||||
|
} else {
|
||||||
|
head = l.next;
|
||||||
|
}
|
||||||
|
// keep iterating
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// distinct signatures
|
||||||
|
prev = l;
|
||||||
|
// keep iterating
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// append new method to the list
|
||||||
|
if (prev == null) {
|
||||||
|
head = new MethodList(method);
|
||||||
|
} else {
|
||||||
|
prev.next = new MethodList(method);
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int length() {
|
||||||
|
int len = 1;
|
||||||
|
for (MethodList ml = next; ml != null; ml = ml.next) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1st method in list with most specific return type
|
||||||
|
*/
|
||||||
|
Method getMostSpecific() {
|
||||||
|
Method m = method;
|
||||||
|
Class<?> rt = m.getReturnType();
|
||||||
|
for (MethodList ml = next; ml != null; ml = ml.next) {
|
||||||
|
Method m2 = ml.method;
|
||||||
|
Class<?> rt2 = m2.getReturnType();
|
||||||
|
if (rt2 != rt && rt.isAssignableFrom(rt2)) {
|
||||||
|
// found more specific return type
|
||||||
|
m = m2;
|
||||||
|
rt = rt2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -240,6 +240,11 @@ public final class Constructor<T> extends Executable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<?>[] getSharedParameterTypes() {
|
||||||
|
return parameterTypes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -222,6 +222,10 @@ public abstract class Executable extends AccessibleObject
|
||||||
*/
|
*/
|
||||||
public abstract TypeVariable<?>[] getTypeParameters();
|
public abstract TypeVariable<?>[] getTypeParameters();
|
||||||
|
|
||||||
|
// returns shared array of parameter types - must never give it out
|
||||||
|
// to the untrusted code...
|
||||||
|
abstract Class<?>[] getSharedParameterTypes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of {@code Class} objects that represent the formal
|
* Returns an array of {@code Class} objects that represent the formal
|
||||||
* parameter types, in declaration order, of the executable
|
* parameter types, in declaration order, of the executable
|
||||||
|
|
|
@ -290,6 +290,11 @@ public final class Method extends Executable {
|
||||||
} else { return getReturnType();}
|
} else { return getReturnType();}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Class<?>[] getSharedParameterTypes() {
|
||||||
|
return parameterTypes;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -132,6 +132,10 @@ class ReflectAccess implements jdk.internal.reflect.LangReflectAccess {
|
||||||
return ex.getTypeAnnotationBytes();
|
return ex.getTypeAnnotationBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
|
||||||
|
return ex.getSharedParameterTypes();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copying routines, needed to quickly fabricate new Field,
|
// Copying routines, needed to quickly fabricate new Field,
|
||||||
// Method, and Constructor objects from templates
|
// Method, and Constructor objects from templates
|
||||||
|
|
|
@ -96,6 +96,9 @@ public interface LangReflectAccess {
|
||||||
/** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */
|
/** Gets the "parameterAnnotations" field from a Constructor (used for serialization) */
|
||||||
public byte[] getConstructorParameterAnnotations(Constructor<?> c);
|
public byte[] getConstructorParameterAnnotations(Constructor<?> c);
|
||||||
|
|
||||||
|
/** Gets the shared array of parameter types of an Executable. */
|
||||||
|
public Class<?>[] getExecutableSharedParameterTypes(Executable ex);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copying routines, needed to quickly fabricate new Field,
|
// Copying routines, needed to quickly fabricate new Field,
|
||||||
// Method, and Constructor objects from templates
|
// Method, and Constructor objects from templates
|
||||||
|
|
|
@ -345,6 +345,10 @@ public class ReflectionFactory {
|
||||||
return langReflectAccess().getExecutableTypeAnnotationBytes(ex);
|
return langReflectAccess().getExecutableTypeAnnotationBytes(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Class<?>[] getExecutableSharedParameterTypes(Executable ex) {
|
||||||
|
return langReflectAccess().getExecutableSharedParameterTypes(ex);
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Routines used by serialization
|
// Routines used by serialization
|
||||||
|
|
|
@ -49,7 +49,7 @@ interface B3 extends D3 { void m(); }
|
||||||
interface C3 extends D3 { void m(); }
|
interface C3 extends D3 { void m(); }
|
||||||
interface D3 { void m() ; }
|
interface D3 { void m() ; }
|
||||||
|
|
||||||
// B.m, D.m
|
// B.m
|
||||||
interface A4 extends B4, C4 {}
|
interface A4 extends B4, C4 {}
|
||||||
interface B4 extends D4 { void m(); }
|
interface B4 extends D4 { void m(); }
|
||||||
interface C4 extends D4 {}
|
interface C4 extends D4 {}
|
||||||
|
@ -92,7 +92,7 @@ public class StarInheritance {
|
||||||
Class [] l3 = {B3.class, C3.class};
|
Class [] l3 = {B3.class, C3.class};
|
||||||
test(A3.class.getMethods(), new ArrayList(Arrays.asList(l3)));
|
test(A3.class.getMethods(), new ArrayList(Arrays.asList(l3)));
|
||||||
|
|
||||||
Class [] l4 = {B4.class, D4.class};
|
Class [] l4 = {B4.class};
|
||||||
test(A4.class.getMethods(), new ArrayList(Arrays.asList(l4)));
|
test(A4.class.getMethods(), new ArrayList(Arrays.asList(l4)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,8 @@ import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.testng.annotations.DataProvider;
|
import org.testng.annotations.DataProvider;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
@ -55,7 +54,7 @@ public class FilterNotMostSpecific {
|
||||||
for (MethodDesc expected : expectedMethods) {
|
for (MethodDesc expected : expectedMethods) {
|
||||||
if (expected.isGetMethodReturn()) {
|
if (expected.isGetMethodReturn()) {
|
||||||
try {
|
try {
|
||||||
Method m = iface.getMethod(expected.name());
|
Method m = iface.getMethod(expected.name(), expected.parameterTypes());
|
||||||
if (!assertMatch(expected, m))
|
if (!assertMatch(expected, m))
|
||||||
fail(failMsg(expected, m, iface));
|
fail(failMsg(expected, m, iface));
|
||||||
else
|
else
|
||||||
|
@ -72,26 +71,18 @@ public class FilterNotMostSpecific {
|
||||||
public void testGetMethods(Class<?> iface) {
|
public void testGetMethods(Class<?> iface) {
|
||||||
List<Method> foundMethods = filterObjectMethods(iface.getMethods());
|
List<Method> foundMethods = filterObjectMethods(iface.getMethods());
|
||||||
MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class);
|
MethodDesc[] expectedMethods = iface.getAnnotationsByType(MethodDesc.class);
|
||||||
Set<Method> used = new HashSet<>();
|
|
||||||
|
|
||||||
for (MethodDesc expected : expectedMethods) {
|
for (MethodDesc expected : expectedMethods) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
|
|
||||||
for (Method m : foundMethods) {
|
for (Method m : foundMethods) {
|
||||||
if (used.contains(m))
|
if (assertMatch(expected, m)) {
|
||||||
continue;
|
|
||||||
|
|
||||||
if(expected.name().equals(m.getName()) &&
|
|
||||||
expected.declaringClass() ==m.getDeclaringClass()) {
|
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
assertMatch(expected, m);
|
|
||||||
used.add(m);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! found)
|
if (!found)
|
||||||
fail("On: "+ iface +"\nDid not find " + toMethodString(expected) + " among " + foundMethods);
|
fail("On: "+ iface +"\nDid not find " + toMethodString(expected) +
|
||||||
|
" among " + foundMethods);
|
||||||
}
|
}
|
||||||
assertEquals(foundMethods.size(), expectedMethods.length,
|
assertEquals(foundMethods.size(), expectedMethods.length,
|
||||||
"\non: " + iface +
|
"\non: " + iface +
|
||||||
|
@ -104,6 +95,11 @@ public class FilterNotMostSpecific {
|
||||||
return false;
|
return false;
|
||||||
if (expected.declaringClass() != m.getDeclaringClass())
|
if (expected.declaringClass() != m.getDeclaringClass())
|
||||||
return false;
|
return false;
|
||||||
|
if (!Arrays.equals(expected.parameterTypes(), m.getParameterTypes()))
|
||||||
|
return false;
|
||||||
|
if (expected.returnType() != NotSpecified.class &&
|
||||||
|
expected.returnType() != m.getReturnType())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (expected.kind() == MethodKind.ABSTRACT)
|
if (expected.kind() == MethodKind.ABSTRACT)
|
||||||
assertTrue(Modifier.isAbstract(m.getModifiers()), m + " should be ABSTRACT");
|
assertTrue(Modifier.isAbstract(m.getModifiers()), m + " should be ABSTRACT");
|
||||||
|
@ -128,8 +124,13 @@ public class FilterNotMostSpecific {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toMethodString(MethodDesc m) {
|
private String toMethodString(MethodDesc m) {
|
||||||
return m.declaringClass().getSimpleName().toString() + "." +
|
return (m.returnType() != NotSpecified.class
|
||||||
m.name() + "()";
|
? m.returnType().getSimpleName() + " "
|
||||||
|
: "") +
|
||||||
|
m.declaringClass().getSimpleName().toString() + "." +
|
||||||
|
m.name() + Stream.of(m.parameterTypes())
|
||||||
|
.map(cl -> cl.getSimpleName())
|
||||||
|
.collect(Collectors.joining(", ", "(", ")"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<String> toMethodStrings(MethodDesc[] m) {
|
private List<String> toMethodStrings(MethodDesc[] m) {
|
||||||
|
@ -142,11 +143,16 @@ public class FilterNotMostSpecific {
|
||||||
@Repeatable(MethodDescs.class)
|
@Repeatable(MethodDescs.class)
|
||||||
public @interface MethodDesc {
|
public @interface MethodDesc {
|
||||||
String name();
|
String name();
|
||||||
|
Class<?> returnType() default NotSpecified.class;
|
||||||
|
Class<?>[] parameterTypes() default {};
|
||||||
Class<?> declaringClass();
|
Class<?> declaringClass();
|
||||||
MethodKind kind() default MethodKind.ABSTRACT;
|
MethodKind kind() default MethodKind.ABSTRACT;
|
||||||
boolean isGetMethodReturn() default false;
|
boolean isGetMethodReturn() default false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// special type marking a not-specified return type in @MethodDesc
|
||||||
|
interface NotSpecified {}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface MethodDescs {
|
public @interface MethodDescs {
|
||||||
MethodDesc[] value();
|
MethodDesc[] value();
|
||||||
|
@ -172,22 +178,18 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
|
||||||
interface P2 extends Jbis, Jprim {}
|
interface P2 extends Jbis, Jprim {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
@MethodDesc(name="nonDefault", declaringClass=Jbis.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
|
||||||
interface P3 extends Jbis, Jprim, I {}
|
interface P3 extends Jbis, Jprim, I {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
|
||||||
interface P4 extends I, J {}
|
interface P4 extends I, J {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
|
||||||
interface P5 extends J, I {}
|
interface P5 extends J, I {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||||
|
@ -198,14 +200,12 @@ public class FilterNotMostSpecific {
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface K1M extends J { void nonDefault(); }
|
interface K1M extends J { void nonDefault(); }
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
interface K2 extends I, J {}
|
||||||
interface K2 extends I, J {}
|
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
@MethodDesc(name="nonDefault", declaringClass=J.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class)
|
|
||||||
interface K2O extends J, I {}
|
interface K2O extends J, I {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=K2M.class,
|
@MethodDesc(name="nonDefault", declaringClass=K2M.class,
|
||||||
|
@ -244,21 +244,18 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface N2 extends M, L {}
|
interface N2 extends M, L {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=M.class,
|
@MethodDesc(name="isDefault", declaringClass=M.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
interface N22 extends L, M {}
|
interface N22 extends L, M {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2D.class,
|
@MethodDesc(name="isDefault", declaringClass=N2D.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface N2D extends M, L { default void isDefault() {}}
|
interface N2D extends M, L { default void isDefault() {}}
|
||||||
|
@ -277,48 +274,36 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface O1 extends L, M, N2DN {}
|
interface O1 extends L, M, N2DN {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface O2 extends M, N2DN, L {}
|
interface O2 extends M, N2DN, L {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface O3 extends N2DN, L, M {}
|
interface O3 extends N2DN, L, M {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
abstract class C1 implements L, M, N2DN {}
|
abstract class C1 implements L, M, N2DN {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
abstract class C2 implements M, N2DN, L {}
|
abstract class C2 implements M, N2DN, L {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
abstract class C3 implements N2DN, L, M {}
|
abstract class C3 implements N2DN, L, M {}
|
||||||
|
@ -345,88 +330,54 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R1 extends L, M, N2DN { void isDefault(); }
|
interface R1 extends L, M, N2DN { void isDefault(); }
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R2 extends M, N2DN, L { void isDefault(); }
|
interface R2 extends M, N2DN, L { void isDefault(); }
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface R3 extends N2DN, L, M { void isDefault(); }
|
interface R3 extends N2DN, L, M { void isDefault(); }
|
||||||
|
|
||||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
||||||
// default but not the most specific
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R1.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R4 extends L, M, N2DN, R1 {}
|
interface R4 extends L, M, N2DN, R1 {}
|
||||||
|
|
||||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
||||||
// default but not the most specific
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", 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)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R5 extends M, N2DN, R2, L {}
|
interface R5 extends M, N2DN, R2, L {}
|
||||||
|
|
||||||
// this one is strange but logical, getMethod finds N2DN first, which is
|
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
||||||
// default but not the most specific
|
isGetMethodReturn=true)
|
||||||
@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,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface R6 extends N2DN, R3, L, M {}
|
interface R6 extends N2DN, R3, L, M {}
|
||||||
|
|
||||||
// the following three finds the "right" one
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
@MethodDesc(name="isDefault", declaringClass=R1.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R7 extends L, M, R1, N2DN {}
|
interface R7 extends L, M, R1, N2DN {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
@MethodDesc(name="isDefault", declaringClass=R2.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="isDefault", declaringClass=N2DN.class,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=L.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=M.class,
|
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=N2DN.class)
|
|
||||||
interface R8 extends M, R2, N2DN, L {}
|
interface R8 extends M, R2, N2DN, L {}
|
||||||
|
|
||||||
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
@MethodDesc(name="isDefault", declaringClass=R3.class,
|
||||||
isGetMethodReturn=true)
|
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,
|
@MethodDesc(name="nonDefault", declaringClass=N2DN.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
interface R9 extends R3, N2DN, L, M {}
|
interface R9 extends R3, N2DN, L, M {}
|
||||||
|
@ -445,51 +396,40 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
interface Z3 extends Z2, Z1 { void z(); }
|
interface Z3 extends Z2, Z1 { void z(); }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
|
||||||
interface Z41 extends Z1, Z2, Z3 { }
|
interface Z41 extends Z1, Z2, Z3 { }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
|
||||||
interface Z42 extends Z2, Z3, Z1 { }
|
interface Z42 extends Z2, Z3, Z1 { }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
interface Z43 extends Z3, Z1, Z2 { }
|
interface Z43 extends Z3, Z1, Z2 { }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
|
||||||
abstract class ZC41 implements Z1, Z2, Z3 { }
|
abstract class ZC41 implements Z1, Z2, Z3 { }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class)
|
|
||||||
abstract class ZC42 implements Z2, Z3, Z1 { }
|
abstract class ZC42 implements Z2, Z3, Z1 { }
|
||||||
|
|
||||||
@MethodDesc(name="z", declaringClass=Z3.class,
|
@MethodDesc(name="z", declaringClass=Z3.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn = true)
|
||||||
@MethodDesc(name="z", declaringClass=Z2.class,
|
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
abstract class ZC43 implements Z3, Z1, Z2 { }
|
abstract class ZC43 implements Z3, Z1, Z2 { }
|
||||||
|
|
||||||
// More reabstraction + concretization
|
// More reabstraction + concretization
|
||||||
interface X1 { default void x() {} }
|
interface X1 { default void x() {} }
|
||||||
interface X2 extends X1 { void x(); }
|
interface X2 extends X1 { void x(); }
|
||||||
|
|
||||||
@MethodDesc(name="x", declaringClass=X1.class,
|
@MethodDesc(name="x", declaringClass=X2.class,
|
||||||
kind=MethodKind.DEFAULT, isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="x", declaringClass=X2.class)
|
|
||||||
interface X31 extends X1, X2 {}
|
interface X31 extends X1, X2 {}
|
||||||
|
|
||||||
@MethodDesc(name="x", declaringClass=X2.class,
|
@MethodDesc(name="x", declaringClass=X2.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="x", declaringClass=X1.class,
|
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
interface X32 extends X2, X1 {}
|
interface X32 extends X2, X1 {}
|
||||||
|
|
||||||
@MethodDesc(name="x", declaringClass=X3.class,
|
@MethodDesc(name="x", declaringClass=X3.class,
|
||||||
|
@ -524,85 +464,107 @@ public class FilterNotMostSpecific {
|
||||||
|
|
||||||
interface K extends I, J { void nonDefault(); }
|
interface K extends I, J { void nonDefault(); }
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
@MethodDesc(name="nonDefault", declaringClass=K.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
|
||||||
abstract class ZZ1 implements I, J, K {}
|
abstract class ZZ1 implements I, J, K {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
@MethodDesc(name="nonDefault", declaringClass=K.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
|
||||||
abstract class ZZ2 extends ZZ1 implements K, I, J {}
|
abstract class ZZ2 extends ZZ1 implements K, I, J {}
|
||||||
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=I.class,
|
@MethodDesc(name="nonDefault", declaringClass=K.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="nonDefault", declaringClass=J.class)
|
|
||||||
@MethodDesc(name="nonDefault", declaringClass=K.class)
|
|
||||||
abstract class ZZ3 extends ZZ2 implements J, K, I {}
|
abstract class ZZ3 extends ZZ2 implements J, K, I {}
|
||||||
|
|
||||||
// bridges
|
// bridges...
|
||||||
interface B1A { Object m(); }
|
|
||||||
interface B1B extends B1A { Map m(); }
|
|
||||||
|
|
||||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
interface B1 { Object m(); }
|
||||||
isGetMethodReturn=true)
|
interface B2A extends B1 { Map m(); }
|
||||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
interface B2B extends B1 { HashMap m(); }
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
@MethodDesc(name="m", declaringClass=B1C.class,
|
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
interface B1C extends B1B { HashMap m(); }
|
|
||||||
|
|
||||||
@MethodDesc(name="m", declaringClass=B2.class,
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B3A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B3A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass=B3A.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="m", declaringClass=B2.class,
|
interface B3A extends B2A { HashMap m(); }
|
||||||
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
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B4A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B4A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass= B4A.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="m", declaringClass=B2.class, //Map
|
interface B4A extends B3A { HashMap m(); }
|
||||||
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", returnType=Object.class, declaringClass=B4A.class,
|
||||||
@MethodDesc(name="m", declaringClass=B2.class, //HashMap
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B4A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass= B4A.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="m", declaringClass=B2.class, //Map
|
interface B5A2 extends B4A, B1 {}
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
@MethodDesc(name="m", declaringClass=B2.class, //Object
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B4A.class,
|
||||||
kind=MethodKind.DEFAULT)
|
kind = MethodKind.DEFAULT)
|
||||||
@MethodDesc(name="m", declaringClass=B1C.class) //HashMap
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B4A.class,
|
||||||
interface B3B extends B2, B1C {}
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass= B4A.class,
|
||||||
|
isGetMethodReturn=true)
|
||||||
|
interface B5A4A extends B4A, B3A {}
|
||||||
|
|
||||||
|
// ... + most specific return type for getMethod from two unrelated interfaces
|
||||||
|
|
||||||
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B2A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B2B.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B2A.class)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass=B2B.class,
|
||||||
|
isGetMethodReturn=true)
|
||||||
|
interface B3AB extends B2A, B2B {}
|
||||||
|
|
||||||
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B2A.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Object.class, declaringClass=B2B.class,
|
||||||
|
kind = MethodKind.DEFAULT)
|
||||||
|
@MethodDesc(name="m", returnType=Map.class, declaringClass=B2A.class)
|
||||||
|
@MethodDesc(name="m", returnType=HashMap.class, declaringClass=B2B.class,
|
||||||
|
isGetMethodReturn=true)
|
||||||
|
interface B3BA extends B2B, B2A {}
|
||||||
|
|
||||||
// same name different params type
|
// same name different params type
|
||||||
interface A1 { void m(); void m(int i); void m(int i, int j); }
|
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 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) {} }
|
interface A2B extends A1 { void m(); void m(int i); default void m(int i, int j) {} }
|
||||||
|
|
||||||
@MethodDesc(name="m", declaringClass=A1.class,
|
@MethodDesc(name="m", parameterTypes = {}, declaringClass=A2A.class,
|
||||||
|
isGetMethodReturn=true)
|
||||||
|
@MethodDesc(name="m", parameterTypes = {int.class}, declaringClass=A2A.class,
|
||||||
|
isGetMethodReturn=true)
|
||||||
|
@MethodDesc(name="m", parameterTypes = {int.class, int.class}, declaringClass=A2A.class,
|
||||||
isGetMethodReturn=true)
|
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 {}
|
interface A3A extends A1, A2A {}
|
||||||
|
|
||||||
@MethodDesc(name="m", declaringClass=A1.class,
|
@MethodDesc(name="m", parameterTypes = {}, declaringClass=A2B.class,
|
||||||
isGetMethodReturn=true)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="m", declaringClass=A1.class)
|
@MethodDesc(name="m", parameterTypes = {int.class}, declaringClass=A2B.class,
|
||||||
@MethodDesc(name="m", declaringClass=A2B.class)
|
isGetMethodReturn=true)
|
||||||
@MethodDesc(name="m", declaringClass=A2B.class)
|
@MethodDesc(name="m", parameterTypes = {int.class, int.class}, declaringClass=A2B.class,
|
||||||
@MethodDesc(name="m", declaringClass=A2B.class,
|
kind = MethodKind.DEFAULT, isGetMethodReturn=true)
|
||||||
kind=MethodKind.DEFAULT)
|
|
||||||
interface A3B extends A1, A2B {}
|
interface A3B extends A1, A2B {}
|
||||||
|
|
||||||
|
// method in directly implemented interface overrides interface method
|
||||||
|
// inherited by superclass
|
||||||
|
|
||||||
|
interface E { void m(); }
|
||||||
|
interface F extends E { void m(); }
|
||||||
|
abstract class G implements E {}
|
||||||
|
|
||||||
|
@MethodDesc(name="m", declaringClass=F.class, isGetMethodReturn=true)
|
||||||
|
abstract class H extends G implements F {}
|
||||||
|
|
||||||
@DataProvider
|
@DataProvider
|
||||||
public Object[][] getCases() { return CASES; }
|
public Object[][] getCases() { return CASES; }
|
||||||
public static final Class<?>[][] CASES = {
|
public static final Class<?>[][] CASES = {
|
||||||
|
@ -680,12 +642,16 @@ public class FilterNotMostSpecific {
|
||||||
{ XC42.class },
|
{ XC42.class },
|
||||||
{ XC43.class },
|
{ XC43.class },
|
||||||
|
|
||||||
{ B1C.class },
|
|
||||||
{ B2.class },
|
|
||||||
{ B3A.class },
|
{ B3A.class },
|
||||||
{ B3B.class },
|
{ B4A.class },
|
||||||
|
{ B5A2.class },
|
||||||
|
{ B5A4A.class },
|
||||||
|
{ B3AB.class },
|
||||||
|
{ B3BA.class },
|
||||||
|
|
||||||
{ A3A.class },
|
{ A3A.class },
|
||||||
{ A3B.class },
|
{ A3B.class },
|
||||||
|
|
||||||
|
{ H.class },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
1379
jdk/test/java/lang/reflect/PublicMethods/Case1.results
Normal file
1379
jdk/test/java/lang/reflect/PublicMethods/Case1.results
Normal file
File diff suppressed because it is too large
Load diff
529
jdk/test/java/lang/reflect/PublicMethods/PublicMethodsTest.java
Normal file
529
jdk/test/java/lang/reflect/PublicMethods/PublicMethodsTest.java
Normal file
|
@ -0,0 +1,529 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.tools.Diagnostic;
|
||||||
|
import javax.tools.DiagnosticListener;
|
||||||
|
import javax.tools.FileObject;
|
||||||
|
import javax.tools.ForwardingJavaFileManager;
|
||||||
|
import javax.tools.JavaCompiler;
|
||||||
|
import javax.tools.JavaFileObject;
|
||||||
|
import javax.tools.SimpleJavaFileObject;
|
||||||
|
import javax.tools.StandardJavaFileManager;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
import javax.tools.ToolProvider;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8062389
|
||||||
|
* @summary Nearly exhaustive test of Class.getMethod() and Class.getMethods()
|
||||||
|
* @run main PublicMethodsTest
|
||||||
|
*/
|
||||||
|
public class PublicMethodsTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Case c = new Case1();
|
||||||
|
|
||||||
|
int[] diffs = new int[1];
|
||||||
|
try (Stream<Map.Entry<int[], Map<String, String>>>
|
||||||
|
expected = expectedResults(c)) {
|
||||||
|
diffResults(c, expected)
|
||||||
|
.forEach(diff -> {
|
||||||
|
System.out.println(diff);
|
||||||
|
diffs[0]++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diffs[0] > 0) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"There were " + diffs[0] + " differences.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use this to generate .results file for particular case
|
||||||
|
public static class Generate {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Case c = new Case1();
|
||||||
|
dumpResults(generateResults(c))
|
||||||
|
.forEach(System.out::println);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Case {
|
||||||
|
Pattern PLACEHOLDER_PATTERN = Pattern.compile("\\$\\{(.+?)}");
|
||||||
|
|
||||||
|
// possible variants of interface method
|
||||||
|
List<String> INTERFACE_METHODS = List.of(
|
||||||
|
"", "void m();", "default void m() {}", "static void m() {}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// possible variants of class method
|
||||||
|
List<String> CLASS_METHODS = List.of(
|
||||||
|
"", "public abstract void m();",
|
||||||
|
"public void m() {}", "public static void m() {}"
|
||||||
|
);
|
||||||
|
|
||||||
|
// template with placeholders parsed with PLACEHOLDER_PATTERN
|
||||||
|
String template();
|
||||||
|
|
||||||
|
// map of replacementKey (== PLACEHOLDER_PATTERN captured group #1) ->
|
||||||
|
// list of possible replacements
|
||||||
|
Map<String, List<String>> replacements();
|
||||||
|
|
||||||
|
// ordered list of replacement keys
|
||||||
|
List<String> replacementKeys();
|
||||||
|
|
||||||
|
// names of types occurring in the template
|
||||||
|
List<String> classNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Case1 implements Case {
|
||||||
|
|
||||||
|
private static final String TEMPLATE = Stream.of(
|
||||||
|
"interface I { ${I} }",
|
||||||
|
"interface J { ${J} }",
|
||||||
|
"interface K extends I, J { ${K} }",
|
||||||
|
"abstract class C { ${C} }",
|
||||||
|
"abstract class D extends C implements I { ${D} }",
|
||||||
|
"abstract class E extends D implements J, K { ${E} }"
|
||||||
|
).collect(joining("\n"));
|
||||||
|
|
||||||
|
private static final Map<String, List<String>> REPLACEMENTS = Map.of(
|
||||||
|
"I", INTERFACE_METHODS,
|
||||||
|
"J", INTERFACE_METHODS,
|
||||||
|
"K", INTERFACE_METHODS,
|
||||||
|
"C", CLASS_METHODS,
|
||||||
|
"D", CLASS_METHODS,
|
||||||
|
"E", CLASS_METHODS
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final List<String> REPLACEMENT_KEYS = REPLACEMENTS
|
||||||
|
.keySet().stream().sorted().collect(Collectors.toList());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String template() {
|
||||||
|
return TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> replacements() {
|
||||||
|
return REPLACEMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> replacementKeys() {
|
||||||
|
return REPLACEMENT_KEYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> classNames() {
|
||||||
|
// just by accident, names of classes are equal to replacement keys
|
||||||
|
// (this need not be the case in general)
|
||||||
|
return REPLACEMENT_KEYS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate all combinations as a tuple of indexes into lists of
|
||||||
|
// replacements. The index of the element in int[] tuple represents the index
|
||||||
|
// of the key in replacementKeys() list. The value of the element in int[] tuple
|
||||||
|
// represents the index of the replacement string in list of strings in the
|
||||||
|
// value of the entry of replacements() map with the corresponding key.
|
||||||
|
static Stream<int[]> combinations(Case c) {
|
||||||
|
int[] sizes = c.replacementKeys().stream()
|
||||||
|
.mapToInt(key -> c.replacements().get(key).size())
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
return Stream.iterate(
|
||||||
|
new int[sizes.length],
|
||||||
|
state -> state != null,
|
||||||
|
state -> {
|
||||||
|
int[] newState = state.clone();
|
||||||
|
for (int i = 0; i < state.length; i++) {
|
||||||
|
if (++newState[i] < sizes[i]) {
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
newState[i] = 0;
|
||||||
|
}
|
||||||
|
// wrapped-around
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// given the combination of indexes, return the expanded template
|
||||||
|
static String expandTemplate(Case c, int[] combination) {
|
||||||
|
|
||||||
|
// 1st create a map: key -> replacement string
|
||||||
|
Map<String, String> map = new HashMap<>(combination.length * 4 / 3 + 1);
|
||||||
|
for (int i = 0; i < combination.length; i++) {
|
||||||
|
String key = c.replacementKeys().get(i);
|
||||||
|
String repl = c.replacements().get(key).get(combination[i]);
|
||||||
|
map.put(key, repl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Case.PLACEHOLDER_PATTERN
|
||||||
|
.matcher(c.template())
|
||||||
|
.replaceAll(match -> map.get(match.group(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compile expanded template into a ClassLoader that sees compiled classes
|
||||||
|
*/
|
||||||
|
static ClassLoader compile(String source) throws CompileException {
|
||||||
|
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
|
||||||
|
if (javac == null) {
|
||||||
|
throw new AssertionError("No Java compiler tool found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorsCollector errorsCollector = new ErrorsCollector();
|
||||||
|
StandardJavaFileManager standardJavaFileManager =
|
||||||
|
javac.getStandardFileManager(errorsCollector, Locale.ROOT,
|
||||||
|
Charset.forName("UTF-8"));
|
||||||
|
TestFileManager testFileManager = new TestFileManager(
|
||||||
|
standardJavaFileManager, source);
|
||||||
|
|
||||||
|
JavaCompiler.CompilationTask javacTask;
|
||||||
|
try {
|
||||||
|
javacTask = javac.getTask(
|
||||||
|
null, // use System.err
|
||||||
|
testFileManager,
|
||||||
|
errorsCollector,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(testFileManager.getJavaFileForInput(
|
||||||
|
StandardLocation.SOURCE_PATH,
|
||||||
|
TestFileManager.TEST_CLASS_NAME,
|
||||||
|
JavaFileObject.Kind.SOURCE))
|
||||||
|
);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
javacTask.call();
|
||||||
|
|
||||||
|
if (errorsCollector.hasError()) {
|
||||||
|
throw new CompileException(errorsCollector.getErrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TestClassLoader(ClassLoader.getSystemClassLoader(),
|
||||||
|
testFileManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CompileException extends Exception {
|
||||||
|
CompileException(List<Diagnostic<?>> diagnostics) {
|
||||||
|
super(diagnostics.stream()
|
||||||
|
.map(diag -> diag.toString())
|
||||||
|
.collect(Collectors.joining("\n")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestFileManager
|
||||||
|
extends ForwardingJavaFileManager<StandardJavaFileManager> {
|
||||||
|
static final String TEST_CLASS_NAME = "Test";
|
||||||
|
|
||||||
|
private final String testSource;
|
||||||
|
private final Map<String, ClassFileObject> classes = new HashMap<>();
|
||||||
|
|
||||||
|
TestFileManager(StandardJavaFileManager fileManager, String source) {
|
||||||
|
super(fileManager);
|
||||||
|
testSource = "public class " + TEST_CLASS_NAME + " {}\n" +
|
||||||
|
source; // the rest of classes are package-private
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaFileObject getJavaFileForInput(Location location,
|
||||||
|
String className,
|
||||||
|
JavaFileObject.Kind kind)
|
||||||
|
throws IOException {
|
||||||
|
if (location == StandardLocation.SOURCE_PATH &&
|
||||||
|
kind == JavaFileObject.Kind.SOURCE &&
|
||||||
|
TEST_CLASS_NAME.equals(className)) {
|
||||||
|
return new SourceFileObject(className, testSource);
|
||||||
|
}
|
||||||
|
return super.getJavaFileForInput(location, className, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SourceFileObject extends SimpleJavaFileObject {
|
||||||
|
private final String source;
|
||||||
|
|
||||||
|
SourceFileObject(String className, String source) {
|
||||||
|
super(
|
||||||
|
URI.create("memory:/src/" +
|
||||||
|
className.replace('.', '/') + ".java"),
|
||||||
|
Kind.SOURCE
|
||||||
|
);
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaFileObject getJavaFileForOutput(Location location,
|
||||||
|
String className,
|
||||||
|
JavaFileObject.Kind kind,
|
||||||
|
FileObject sibling)
|
||||||
|
throws IOException {
|
||||||
|
if (kind == JavaFileObject.Kind.CLASS) {
|
||||||
|
ClassFileObject cfo = new ClassFileObject(className);
|
||||||
|
classes.put(className, cfo);
|
||||||
|
return cfo;
|
||||||
|
}
|
||||||
|
return super.getJavaFileForOutput(location, className, kind, sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassFileObject extends SimpleJavaFileObject {
|
||||||
|
final String className;
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream;
|
||||||
|
|
||||||
|
ClassFileObject(String className) {
|
||||||
|
super(
|
||||||
|
URI.create("memory:/out/" +
|
||||||
|
className.replace('.', '/') + ".class"),
|
||||||
|
Kind.CLASS
|
||||||
|
);
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream openOutputStream() throws IOException {
|
||||||
|
return byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getBytes() {
|
||||||
|
if (byteArrayOutputStream == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No class file written for class: " + className);
|
||||||
|
}
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] getClassBytes(String className) {
|
||||||
|
ClassFileObject cfo = classes.get(className);
|
||||||
|
return (cfo == null) ? null : cfo.getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ErrorsCollector implements DiagnosticListener<JavaFileObject> {
|
||||||
|
private final List<Diagnostic<?>> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
|
||||||
|
if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
|
||||||
|
errors.add(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasError() {
|
||||||
|
return !errors.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Diagnostic<?>> getErrors() {
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestClassLoader extends ClassLoader {
|
||||||
|
private final TestFileManager fileManager;
|
||||||
|
|
||||||
|
public TestClassLoader(ClassLoader parent, TestFileManager fileManager) {
|
||||||
|
super(parent);
|
||||||
|
this.fileManager = fileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
|
byte[] classBytes = fileManager.getClassBytes(name);
|
||||||
|
if (classBytes == null) {
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
}
|
||||||
|
return defineClass(name, classBytes, 0, classBytes.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, String> generateResult(Case c, ClassLoader cl) {
|
||||||
|
return
|
||||||
|
c.classNames()
|
||||||
|
.stream()
|
||||||
|
.map(cn -> {
|
||||||
|
try {
|
||||||
|
return Class.forName(cn, false, cl);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException("Class not found: " + cn, e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatMap(clazz -> Stream.of(
|
||||||
|
Map.entry(clazz.getName() + ".gM", generateGetMethodResult(clazz)),
|
||||||
|
Map.entry(clazz.getName() + ".gMs", generateGetMethodsResult(clazz))
|
||||||
|
))
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String generateGetMethodResult(Class<?> clazz) {
|
||||||
|
try {
|
||||||
|
Method m = clazz.getMethod("m");
|
||||||
|
return m.getDeclaringClass().getName() + "." + m.getName();
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String generateGetMethodsResult(Class<?> clazz) {
|
||||||
|
return Stream.of(clazz.getMethods())
|
||||||
|
.filter(m -> m.getDeclaringClass() != Object.class)
|
||||||
|
.map(m -> m.getDeclaringClass().getName()
|
||||||
|
+ "." + m.getName())
|
||||||
|
.collect(Collectors.joining(", ", "[", "]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Map.Entry<int[], Map<String, String>>> generateResults(Case c) {
|
||||||
|
return combinations(c)
|
||||||
|
.flatMap(comb -> {
|
||||||
|
String src = expandTemplate(c, comb);
|
||||||
|
ClassLoader cl;
|
||||||
|
try {
|
||||||
|
cl = compile(src);
|
||||||
|
} catch (CompileException e) {
|
||||||
|
// ignore uncompilable combinations
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
// compilation was successful -> generate result
|
||||||
|
return Stream.of(Map.entry(
|
||||||
|
comb,
|
||||||
|
generateResult(c, cl)
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Map.Entry<int[], Map<String, String>>> expectedResults(Case c) {
|
||||||
|
try {
|
||||||
|
BufferedReader r = new BufferedReader(new InputStreamReader(
|
||||||
|
c.getClass().getResourceAsStream(
|
||||||
|
c.getClass().getSimpleName() + ".results"),
|
||||||
|
"UTF-8"
|
||||||
|
));
|
||||||
|
|
||||||
|
return parseResults(r.lines())
|
||||||
|
.onClose(() -> {
|
||||||
|
try {
|
||||||
|
r.close();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
throw new UncheckedIOException(ioe);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Map.Entry<int[], Map<String, String>>> parseResults(
|
||||||
|
Stream<String> lines
|
||||||
|
) {
|
||||||
|
return lines
|
||||||
|
.map(l -> l.split(Pattern.quote("#")))
|
||||||
|
.map(lkv -> Map.entry(
|
||||||
|
Stream.of(lkv[0].split(Pattern.quote(",")))
|
||||||
|
.mapToInt(Integer::parseInt)
|
||||||
|
.toArray(),
|
||||||
|
Stream.of(lkv[1].split(Pattern.quote("|")))
|
||||||
|
.map(e -> e.split(Pattern.quote("=")))
|
||||||
|
.collect(toMap(ekv -> ekv[0], ekv -> ekv[1]))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<String> dumpResults(
|
||||||
|
Stream<Map.Entry<int[], Map<String, String>>> results
|
||||||
|
) {
|
||||||
|
return results
|
||||||
|
.map(le ->
|
||||||
|
IntStream.of(le.getKey())
|
||||||
|
.mapToObj(String::valueOf)
|
||||||
|
.collect(joining(","))
|
||||||
|
+ "#" +
|
||||||
|
le.getValue().entrySet().stream()
|
||||||
|
.map(e -> e.getKey() + "=" + e.getValue())
|
||||||
|
.collect(joining("|"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<String> diffResults(
|
||||||
|
Case c,
|
||||||
|
Stream<Map.Entry<int[], Map<String, String>>> expectedResults
|
||||||
|
) {
|
||||||
|
return expectedResults
|
||||||
|
.flatMap(exp -> {
|
||||||
|
int[] comb = exp.getKey();
|
||||||
|
Map<String, String> expected = exp.getValue();
|
||||||
|
|
||||||
|
String src = expandTemplate(c, comb);
|
||||||
|
ClassLoader cl;
|
||||||
|
try {
|
||||||
|
cl = compile(src);
|
||||||
|
} catch (CompileException ce) {
|
||||||
|
return Stream.of(src + "\n" +
|
||||||
|
"got compilation error: " + ce);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> actual = generateResult(c, cl);
|
||||||
|
if (actual.equals(expected)) {
|
||||||
|
return Stream.empty();
|
||||||
|
} else {
|
||||||
|
Map<String, String> diff = new HashMap<>(expected);
|
||||||
|
diff.entrySet().removeAll(actual.entrySet());
|
||||||
|
return Stream.of(
|
||||||
|
diff.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(e -> "expected: " + e.getKey() + ": " +
|
||||||
|
e.getValue() + "\n" +
|
||||||
|
" actual: " + e.getKey() + ": " +
|
||||||
|
actual.get(e.getKey()) + "\n")
|
||||||
|
.collect(joining("\n", src + "\n\n", "\n"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue