src/java.base/share/classes/java/lang/Class.java

Print this page

        

@@ -43,11 +43,10 @@
 import java.io.InputStream;
 import java.io.ObjectStreamField;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Set;

@@ -409,11 +408,13 @@
                     "Can not call newInstance() on the Class for java.lang.Class"
                 );
             }
             try {
                 Class<?>[] empty = {};
-                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
+                // must copy constructor since we are modifying it
+                final Constructor<T> c = getReflectionFactory().copyConstructor(
+                    getConstructor0(empty, Member.DECLARED));
                 // Disable accessibility checks on the constructor
                 // since we have to do the security check here anyway
                 // (the stack depth is wrong for the Constructor's
                 // security check to work)
                 java.security.AccessController.doPrivileged(

@@ -840,22 +841,26 @@
      * returned in that order.
      *
      * @return an array of interfaces implemented by this class.
      */
     public Class<?>[] getInterfaces() {
+        return getInterfaces(true);
+    }
+
+    private Class<?>[] getInterfaces(boolean cloneArray) {
         ReflectionData<T> rd = reflectionData();
         if (rd == null) {
             // no cloning required
             return getInterfaces0();
         } else {
             Class<?>[] interfaces = rd.interfaces;
             if (interfaces == null) {
                 interfaces = getInterfaces0();
                 rd.interfaces = interfaces;
             }
-            // defensively copy before handing over to user code
-            return interfaces.clone();
+            // defensively copy cached array before handing over to user code
+            return cloneArray ? interfaces.clone() : interfaces;
         }
     }
 
     private native Class<?>[] getInterfaces0();
 

@@ -1708,11 +1713,11 @@
         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
         Field field = getField0(name);
         if (field == null) {
             throw new NoSuchFieldException(name);
         }
-        return field;
+        return getReflectionFactory().copyField(field);
     }
 
 
     /**
      * Returns a {@code Method} object that reflects the specified public

@@ -1791,11 +1796,11 @@
         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
         Method method = getMethod0(name, parameterTypes, true);
         if (method == null) {
             throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
         }
-        return method;
+        return getReflectionFactory().copyMethod(method);
     }
 
 
     /**
      * Returns a {@code Constructor} object that reflects the specified

@@ -1828,11 +1833,12 @@
      */
     @CallerSensitive
     public Constructor<T> getConstructor(Class<?>... parameterTypes)
         throws NoSuchMethodException, SecurityException {
         checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
-        return getConstructor0(parameterTypes, Member.PUBLIC);
+        return getReflectionFactory().copyConstructor(
+            getConstructor0(parameterTypes, Member.PUBLIC));
     }
 
 
     /**
      * Returns an array of {@code Class} objects reflecting all the

@@ -2075,11 +2081,11 @@
         checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
         Field field = searchFields(privateGetDeclaredFields(false), name);
         if (field == null) {
             throw new NoSuchFieldException(name);
         }
-        return field;
+        return getReflectionFactory().copyField(field);
     }
 
 
     /**
      * Returns a {@code Method} object that reflects the specified

@@ -2135,11 +2141,11 @@
         checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
         Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
         if (method == null) {
             throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
         }
-        return method;
+        return getReflectionFactory().copyMethod(method);
     }
 
 
     /**
      * Returns a {@code Constructor} object that reflects the specified

@@ -2181,11 +2187,12 @@
      */
     @CallerSensitive
     public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
         throws NoSuchMethodException, SecurityException {
         checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
-        return getConstructor0(parameterTypes, Member.DECLARED);
+        return getReflectionFactory().copyConstructor(
+            getConstructor0(parameterTypes, Member.DECLARED));
     }
 
     /**
      * Finds a resource with a given name.  The rules for searching resources
      * associated with a given class are implemented by the defining

@@ -2621,11 +2628,11 @@
         // Local fields
         Field[] tmp = privateGetDeclaredFields(true);
         addAll(fields, tmp);
 
         // Direct superinterfaces, recursively
-        for (Class<?> c : getInterfaces()) {
+        for (Class<?> c : getInterfaces(false)) {
             if (!traversedInterfaces.contains(c)) {
                 traversedInterfaces.add(c);
                 addAll(fields, c.privateGetPublicFields(traversedInterfaces));
             }
         }

@@ -2715,183 +2722,11 @@
             }
         }
         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);
-        }
-    }
-
+    private static final Method[] EMPTY_METHODS = new Method[0];
 
     // Returns an array of "root" methods. These Method objects must NOT
     // be propagated to the outside world, but must instead be copied
     // via ReflectionFactory.copyMethod.
     private Method[] privateGetPublicMethods() {

@@ -2901,56 +2736,63 @@
         if (rd != null) {
             res = rd.publicMethods;
             if (res != null) return res;
         }
 
-        // No cached value available; compute value recursively.
-        // Start by fetching public declared methods
-        MethodArray methods = new MethodArray();
-        {
-            Method[] tmp = privateGetDeclaredMethods(true);
-            methods.addAll(tmp);
+        Method[] declaredMethods = privateGetDeclaredMethods(true);
+        Class<?> superclass = getSuperclass();
+        Class<?>[] interfaces = getInterfaces(false);
+
+        // optimization:
+        // if we don't have a superclass (either we are j.l.Object or an interface)
+        // and don't have (super)interfaces either, then public methods consist
+        // of declared public methods
+        if (superclass == null && interfaces.length == 0) {
+            res = declaredMethods;
+        } else {
+            // we have to do some logic
+            Method[] superclassMethods = (superclass == null)
+                                         ? EMPTY_METHODS
+                                         : superclass.privateGetPublicMethods();
+            Method[][] interfacesMethods = new Method[interfaces.length][];
+            int interfacesMethodsCount = 0;
+            for (int i = 0; i < interfaces.length; i++) {
+                interfacesMethods[i] = interfaces[i].privateGetPublicMethods();
+                interfacesMethodsCount += interfacesMethods[i].length;
+            }
+
+            // ensure enough working capacity
+            // (MethodTable implementation may not support dynamic resizing)
+            MethodTable methodTable = MethodTable.newInstance(
+                declaredMethods.length +
+                superclassMethods.length +
+                interfacesMethodsCount
+            );
+
+            // declared methods first
+            for (Method m : declaredMethods) {
+                methodTable.add(m);
         }
-        // Now recur over superclass and direct superinterfaces.
-        // Go over superinterfaces first so we can more easily filter
-        // out concrete implementations inherited from superclasses at
-        // the end.
-        MethodArray inheritedMethods = new MethodArray();
-        for (Class<?> i : getInterfaces()) {
-            inheritedMethods.addInterfaceMethods(i.privateGetPublicMethods());
+
+            // inherited methods from superclass
+            for (Method m : superclassMethods) {
+                methodTable.addUnlessDeclaredExists(m, this);
         }
-        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
-        for (int i = 0; i < methods.length(); i++) {
-            Method m = methods.get(i);
-            inheritedMethods.removeByNameAndDescriptor(m);
-        }
-        methods.addAllIfNotPresent(inheritedMethods);
-        methods.removeLessSpecifics();
-        methods.compactAndTrim();
-        res = methods.getArray();
+
+            // inherited methods from (super)interfaces
+            for (Method[] ms : interfacesMethods) {
+                for (Method m : ms) {
+                    // interface static methods are not inherited
+                    if (!Modifier.isStatic(m.getModifiers())) {
+                        methodTable.consolidate(m, this);
+                    }
+                }
+            }
+
+            res = methodTable.getMethods();
+        }
+
         if (rd != null) {
             rd.publicMethods = res;
         }
         return res;
     }

@@ -2958,20 +2800,29 @@
 
     //
     // Helpers for fetchers of one field, method, or constructor
     //
 
+    /**
+     * This method does not copy returned 'root' Field object. It MUST be copied
+     * with ReflectionFactory before handed to any code outside java.lang.Class
+     * or modified.
+     */
     private static Field searchFields(Field[] fields, String name) {
         String internedName = name.intern();
         for (Field field : fields) {
             if (field.getName() == internedName) {
-                return getReflectionFactory().copyField(field);
+                return field;
             }
         }
         return null;
     }
 
+    /**
+     * This method returns 'root' Field object. It MUST be copied with ReflectionFactory
+     * before handed to any code outside java.lang.Class or modified.
+     */
     private Field getField0(String name) throws NoSuchFieldException {
         // Note: the intent is that the search algorithm this routine
         // uses be equivalent to the ordering imposed by
         // privateGetPublicFields(). It fetches only the declared
         // public fields for each class, however, to reduce the number

@@ -2982,11 +2833,11 @@
         // Search declared public fields
         if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {
             return res;
         }
         // Direct superinterfaces, recursively
-        Class<?>[] interfaces = getInterfaces();
+        Class<?>[] interfaces = getInterfaces(false);
         for (Class<?> c : interfaces) {
             if ((res = c.getField0(name)) != null) {
                 return res;
             }
         }

@@ -3000,10 +2851,15 @@
             }
         }
         return null;
     }
 
+    /**
+     * This method does not copy returned 'root' Method object. It MUST be copied
+     * with ReflectionFactory before handed to any code outside java.lang.Class
+     * or modified.
+     */
     private static Method searchMethods(Method[] methods,
                                         String name,
                                         Class<?>[] parameterTypes)
     {
         Method res = null;

@@ -3014,77 +2870,72 @@
                 && (res == null
                     || res.getReturnType().isAssignableFrom(m.getReturnType())))
                 res = m;
         }
 
-        return (res == null ? res : getReflectionFactory().copyMethod(res));
-    }
-
-    private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
-        MethodArray interfaceCandidates = new MethodArray(2);
-        Method res =  privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
-        if (res != null)
             return res;
-
-        // Not found on class or superclass directly
-        interfaceCandidates.removeLessSpecifics();
-        return interfaceCandidates.getFirst(); // may be null
     }
 
-    private Method privateGetMethodRecursive(String name,
-            Class<?>[] parameterTypes,
-            boolean includeStaticMethods,
-            MethodArray allInterfaceCandidates) {
+    /**
+     * This method returns 'root' Method object. It MUST be copied with ReflectionFactory
+     * before handed to any code outside java.lang.Class or modified.
+     */
+    private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
         // Note: the intent is that the search algorithm this routine
         // uses be equivalent to the ordering imposed by
         // privateGetPublicMethods(). It fetches only the declared
         // public methods for each class, however, to reduce the
         // number of Method objects which have to be created for the
         // common case where the method being requested is declared in
         // the class which is being queried.
-        //
-        // Due to default methods, unless a method is found on a superclass,
-        // methods declared in any superinterface needs to be considered.
-        // Collect all candidates declared in superinterfaces in {@code
-        // allInterfaceCandidates} and select the most specific if no match on
-        // a superclass is found.
-
-        // Must _not_ return root methods
         Method res;
-        // Search declared public methods
+        // 1st search declared public methods
         if ((res = searchMethods(privateGetDeclaredMethods(true),
                                  name,
-                                 parameterTypes)) != null) {
-            if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
+                                 parameterTypes)) != null &&
+            (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) {
+
+        // 2nd we try the superclass.
+        Class<? super T> superclass = getSuperclass();
+        if (superclass != null &&
+            (res = superclass.getMethod0(name, parameterTypes, includeStaticMethods)) != null) {
                     return res;
                 }
+
+        // last we check (super) interfaces
+        Class<?>[] interfaces = getInterfaces(false);
+        if (interfaces.length == 0) {
+            return null;
+        }
+        // have to construct a MethodTable to consolidate
+        // public methods from (super)interfaces
+        MethodTable methodTable = MethodTable.newInstance(interfaces.length);
+        for (Class<?> c : interfaces) {
+            if ((res = c.getMethod0(name, parameterTypes, false)) != null) {
+                methodTable.consolidate(res, this);
             }
         }
-        // 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;
+
+        // return the first method with the most specific return type
+        return methodTable.getFirstMethodWithMostSpecificReturnType();
     }
 
+    /**
+     * This method returns 'root' Constructor object. It MUST be copied with ReflectionFactory
+     * before handed to any code outside java.lang.Class or modified.
+     */
     private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
                                         int which) throws NoSuchMethodException
     {
         Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
         for (Constructor<T> constructor : constructors) {
             if (arrayContentsEq(parameterTypes,
                                 constructor.getParameterTypes())) {
-                return getReflectionFactory().copyConstructor(constructor);
+                return constructor;
             }
         }
         throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
     }