/* * 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 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; } } }