--- /dev/null 2014-10-16 13:56:36.037090540 +0200 +++ new/src/java.base/share/classes/java/lang/MethodTable.java 2014-11-05 16:59:41.276939043 +0100 @@ -0,0 +1,716 @@ +/* + * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 sun.reflect.ReflectionFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * A table of methods with different operations for adding and removing methods. + * It implements {@link Iterable} which iterates {@link java.lang.reflect.Method} objects + * in order they were added to the table. + */ +interface MethodTable extends Iterable { + + /** + * When requested {@code expectedMaxSize <= MAX_ARRAY_SIZE} then + * {@link java.lang.MethodTable.SimpleArrayImpl} is created else {@link HashArrayImpl} is created. + */ + int MAX_ARRAY_SIZE = 20; + + /** + * Constructs new instance of MethodTable with enough capacity to + * hold {@code expectedMaxSize} methods. + * + * @param expectedMaxSize expected maximum size of MethodTable + * @return new instance of MethodTable + */ + static MethodTable newInstance(int expectedMaxSize) { + return expectedMaxSize <= MAX_ARRAY_SIZE + ? new SimpleArrayImpl(expectedMaxSize) + : new HashArrayImpl(expectedMaxSize); + } + + /** + * Unconditionally adds the {@code method} to this MethodTable. + * + * @param method a method to add + */ + void add(Method method); + + /** + * Adds the {@code method} to this MethodTable unless a method with same + * signature already exists and it's + * {@link java.lang.reflect.Method#getDeclaringClass() declaring class} + * is equal to given {@code declaringClass}. + * + * @param method a method to add + * @param declaringClass the class to check against declaring classes + * of existing methods with same signature + * @return true if method has been added or false if not + */ + boolean addUnlessDeclaredExists(Method method, Class declaringClass); + + /** + * Each existing method with same signature as new {@code method} + * is compared in turn with it and one of the following actions is taken: + * + * + * @param method a method to consolidate with existing methods of same + * signature + */ + void consolidate(Method method, Class declaringClass); + + /** + * @return the size of this MethodTable + */ + int getSize(); + + /** + * @return new array of methods contained in this MethodTable + * in order they were added to this table. + */ + default Method[] getMethods() { + Method[] methods = new Method[getSize()]; + int i = 0; + for (Method m : this) { + methods[i++] = m; + } + return methods; + } + + + /** + * @return the 1st among the methods with the most specific return type + * if there is one or null if there are no methods in this MethodTable. + */ + default Method getFirstMethodWithMostSpecificReturnType() { + Method res = null; + for (Method m : this) { + if (res == null || ( + res.getReturnType() != m.getReturnType() && + res.getReturnType().isAssignableFrom(m.getReturnType()) + )) { + res = m; + } + } + return res; + } + + /** + * A doubly-linked list of nodes. Each DLNode starts life as + * it's own doubly-linked singleton list.

+ * {@link #unlink(Anchor anchor)} un-links a DLNode from a doubly-linked + * list with other nodes and liberates it so that it forms it's + * own singleton list. It also decrements {@code anchor}'s size.

+ * {@link #linkBefore(Anchor anchor)} inserts a DLNode into the doubly-linked + * list before the {@code anchor} and increments it's size. + */ + interface DLNode { + DLNode getNextDln(); + + void setNextDln(DLNode nextDln); + + DLNode getPrevDln(); + + void setPrevDln(DLNode prevDln); + + /** + * Insert {@code this} DLNode before {@code anchor} + */ + default void linkBefore(Anchor anchor) { + // should be free before linking + assert this.getNextDln() == this && + this.getPrevDln() == this; + DLNode prev = anchor.getPrevDln(); + this.setNextDln(anchor); + anchor.setPrevDln(this); + prev.setNextDln(this); + this.setPrevDln(prev); + anchor.addToSize(1); + } + + /** + * Remove {@code this} DLNode from doubly-linked list + */ + default void unlink(Anchor anchor) { + DLNode next = this.getNextDln(); + DLNode prev = this.getPrevDln(); + // should be linked before un-linking + assert next != this && prev != this; + next.setPrevDln(prev); + prev.setNextDln(next); + this.setNextDln(this); + this.setPrevDln(this); + anchor.addToSize(-1); + } + + /** + * Anchor is a {@link DLNode} which keeps track + * of doubly-linked list size. + */ + interface Anchor extends DLNode { + void addToSize(int increment); + } + } + + /** + * A node forming a linked-list of Method objects that share the same signature + * and a key with equals/hashCode that compares method signature. + * It also implements {@link DLNode} so it forms a separate doubly-linked list + * with other DLNode(s) to keep insertion order. + */ + final class MethodNode implements DLNode { + final Method method; + private final int hash; + MethodNode next; + + MethodNode(Method method) { + this.method = method; + this.hash = method.getReturnType().hashCode() ^ + method.getName().hashCode() ^ + reflectionFactory.methodParameterTypesHashCode(method); + } + + void add(MethodNode other, Anchor anchor) { + other.next = this.next; + this.next = other; + other.linkBefore(anchor); + } + + boolean addUnlessDeclaredExists( + MethodNode other, + Class declaringClass, + Anchor anchor + ) { + for ( + MethodNode node = this; + node.method.getDeclaringClass() != declaringClass; + node = node.next + ) { + if (node.next == null) { + node.next = other; + other.linkBefore(anchor); + return true; + } + } + return false; + } + + MethodNode consolidate( + MethodNode other, + Class declaringClass, + Anchor anchor + ) { + Class thisCl = this.method.getDeclaringClass(); + Class otherCl = other.method.getDeclaringClass(); + + if (thisCl == otherCl || // this.method is the same as other.method OR + thisCl == declaringClass) { // this.method is declared by declaringClass + // complete without adding other + return this; + } + + boolean thisAbstract = Modifier.isAbstract(this.method.getModifiers()); + + if (!thisAbstract && // this.method is non-abstract... + otherCl.isAssignableFrom(thisCl)) { // ...and overrides other.method + // complete without adding other + return this; + } + + boolean thisInterface = thisCl.isInterface(); + boolean otherInterface = otherCl.isInterface(); + + if (!thisAbstract && !thisInterface && // this.method is non-abstract class method... + otherInterface) { //...which wins over interface other.method + // complete without adding other + return this; + } + + boolean otherAbstract = Modifier.isAbstract(other.method.getModifiers()); + + if (!otherAbstract && // other.method is non-abstract... + thisCl.isAssignableFrom(otherCl)) { // ...and overrides this.method + // remove this and... + this.unlink(anchor); + if (next == null) { // ...add other + other.linkBefore(anchor); + return other; + } else { // ...continue with other + return next.consolidate(other, declaringClass, anchor); + } + } + + if (!otherAbstract && !otherInterface && // other.method is non-abstract class method... + thisInterface) { //...which wins over interface this.method + // remove this and... + this.unlink(anchor); + if (next == null) { // ...add other + other.linkBefore(anchor); + return other; + } else { // ...continue with other + return next.consolidate(other, declaringClass, anchor); + } + } + + // else keep this and... + if (next == null) { // ...add other + other.linkBefore(anchor); + next = other; + } else { //...continue with other + next = next.consolidate(other, declaringClass, anchor); + } + return this; + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (!(obj instanceof MethodNode)) return false; + MethodNode other = (MethodNode) obj; + return this.method.getReturnType() == other.method.getReturnType() && + this.method.getName() == other.method.getName() && // Method.name is interned + reflectionFactory.methodParameterTypesEquals(this.method, other.method); + } + + // we just need to compare method parameter types + + private static final ReflectionFactory reflectionFactory; + static { + reflectionFactory = + java.security.AccessController.doPrivileged + (new sun.reflect.ReflectionFactory.GetReflectionFactoryAction()); + } + + // DLNode implementation + + private DLNode nextDln = this; + private DLNode prevDln = this; + + @Override + public DLNode getNextDln() { + return nextDln; + } + + @Override + public void setNextDln(DLNode nextDln) { + this.nextDln = nextDln; + } + + @Override + public DLNode getPrevDln() { + return prevDln; + } + + @Override + public void setPrevDln(DLNode prevDln) { + this.prevDln = prevDln; + } + } + + /** + * MethodTable implementation based on {@link java.util.HashMap}. + * It also implements {@link DLNode.Anchor} to + * form a doubly-linked list with other DLNode(s). + */ + final class HashMapImpl extends HashMap implements MethodTable, DLNode.Anchor { + + public HashMapImpl(int expectedMaxSize) { + super(expectedMaxSize * 4 / 3); + } + + @Override + public void add(Method method) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n, n); + if (n0 == null) { + n.linkBefore(this); + } else { + n0.add(n, this); + } + } + + @Override + public boolean addUnlessDeclaredExists(Method method, Class declaringClass) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n, n); + if (n0 == null) { + n.linkBefore(this); + return true; + } else { + return n0.addUnlessDeclaredExists(n, declaringClass, this); + } + } + + @Override + public void consolidate(Method method, Class declaringClass) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n, n); + if (n0 == null) { + n.linkBefore(this); + } else { + MethodNode n1 = n0.consolidate(n, declaringClass, this); + if (n1 != n0) { + put(n1, n1); + } + } + } + + @Override + public int getSize() { + return size; + } + + // Iterable implementation + + @Override + public Iterator iterator() { + return new Iterator() { + + DLNode dln = getNextDln(); + + @Override + public boolean hasNext() { + return dln != HashMapImpl.this; + } + + @Override + public Method next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Method result = ((MethodNode) dln).method; + dln = dln.getNextDln(); + return result; + } + }; + } + + // DLNode.Anchor implementation + + private DLNode nextDln = this; + private DLNode prevDln = this; + private int size; + + @Override + public DLNode getNextDln() { + return nextDln; + } + + @Override + public void setNextDln(DLNode nextDln) { + this.nextDln = nextDln; + } + + @Override + public DLNode getPrevDln() { + return prevDln; + } + + @Override + public void setPrevDln(DLNode prevDln) { + this.prevDln = prevDln; + } + + @Override + public void addToSize(int increment) { + size += increment; + } + } + + /** + * MethodTable implementation based on {@link HashArray}. + * It also implements {@link DLNode.Anchor} to + * form a doubly-linked list with other DLNode(s). + */ + final class HashArrayImpl extends HashArray implements MethodTable, DLNode.Anchor { + + public HashArrayImpl(int expectedMaxSize) { + super(expectedMaxSize); + } + + @Override + public void add(Method method) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n); + if (n0 == null) { + n.linkBefore(this); + } else { + n0.add(n, this); + } + } + + @Override + public boolean addUnlessDeclaredExists(Method method, Class declaringClass) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n); + if (n0 == null) { + n.linkBefore(this); + return true; + } else { + return n0.addUnlessDeclaredExists(n, declaringClass, this); + } + } + + @Override + public void consolidate(Method method, Class declaringClass) { + MethodNode n = new MethodNode(method); + MethodNode n0 = putIfAbsent(n); + if (n0 == null) { + n.linkBefore(this); + } else { + MethodNode n1 = n0.consolidate(n, declaringClass, this); + if (n1 != n0) { + put(n1); + } + } + } + + @Override + public int getSize() { + return size; + } + + // Iterable implementation + + @Override + public Iterator iterator() { + return new Iterator() { + + DLNode dln = getNextDln(); + + @Override + public boolean hasNext() { + return dln != HashArrayImpl.this; + } + + @Override + public Method next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Method result = ((MethodNode) dln).method; + dln = dln.getNextDln(); + return result; + } + }; + } + + // DLNode.Anchor implementation + + private DLNode nextDln = this; + private DLNode prevDln = this; + private int size; + + @Override + public DLNode getNextDln() { + return nextDln; + } + + @Override + public void setNextDln(DLNode nextDln) { + this.nextDln = nextDln; + } + + @Override + public DLNode getPrevDln() { + return prevDln; + } + + @Override + public void setPrevDln(DLNode prevDln) { + this.prevDln = prevDln; + } + + @Override + public void addToSize(int increment) { + size += increment; + } + } + + /** + * Simple array based MethodTable implementation for small number of methods. + */ + final class SimpleArrayImpl implements MethodTable { + + private final Method[] methods; + private int hwm; // High Water Mark + + SimpleArrayImpl(int expectedMaxSize) { + methods = new Method[expectedMaxSize]; + } + + @Override + public void add(Method method) { + methods[hwm++] = method; + } + + @Override + public boolean addUnlessDeclaredExists(Method otherM, Class declaringClass) { + for (int i = 0; i < hwm; i++) { + Method thisM = methods[i]; + if (thisM == null || // mind the gap + thisM.getReturnType() != otherM.getReturnType() || // not same signature + thisM.getName() != otherM.getName() || // Method.name is interned + !MethodNode.reflectionFactory.methodParameterTypesEquals(thisM, otherM)) { + continue; + } + // thisM has same signature as otherM + + if (thisM.getDeclaringClass() == declaringClass) { + return false; + } + // continue with next thisM + } + // add otherM + methods[hwm++] = otherM; + return true; + } + + @Override + public void consolidate(Method otherM, Class declaringClass) { + for (int i = 0; i < hwm; i++) { + Method thisM = methods[i]; + if (thisM == null || // mind the gap + thisM.getReturnType() != otherM.getReturnType() || // not same signature + thisM.getName() != otherM.getName() || // Method.name is interned + !MethodNode.reflectionFactory.methodParameterTypesEquals(thisM, otherM)) { + continue; + } + // thisM has same signature as otherM + + Class thisCl = thisM.getDeclaringClass(); + Class otherCl = otherM.getDeclaringClass(); + + if (thisCl == otherCl || // thisM is the same as otherM OR + thisCl == declaringClass) { // thisM is declared by declaringClass + // complete without adding otherM + return; + } + + boolean thisAbstract = Modifier.isAbstract(thisM.getModifiers()); + + if (!thisAbstract && // thisM is non-abstract... + otherCl.isAssignableFrom(thisCl)) { // ...and overrides otherM + // complete without adding otherM + return; + } + + boolean thisInterface = thisCl.isInterface(); + boolean otherInterface = otherCl.isInterface(); + + if (!thisAbstract && !thisInterface && // this.method is non-abstract class method... + otherInterface) { //...which wins over interface other.method + // complete without adding otherM + return; + } + + boolean otherAbstract = Modifier.isAbstract(otherM.getModifiers()); + + if (!otherAbstract && // other.method is non-abstract... + thisCl.isAssignableFrom(otherCl)) { // ...and overrides this.method + // remove this + methods[i] = null; + continue; + } + + if (!otherAbstract && !otherInterface && // other.method is non-abstract class method... + thisInterface) { //...which wins over interface this.method + // remove this + methods[i] = null; + } + // continue with next thisM + } + // add otherM + methods[hwm++] = otherM; + } + + @Override + public int getSize() { + int size = 0; + for (int i = 0; i < hwm; i++) { + if (methods[i] != null) { + size++; + } + } + return size; + } + + @Override + public Iterator iterator() { + return new Iterator() { + int i = 0; + Method next; + + private Method nextMethod() { + while (i < hwm && next == null) next = methods[i++]; + return next; + } + + @Override + public boolean hasNext() { + return nextMethod() != null; + } + + @Override + public Method next() { + Method m = nextMethod(); + if (m == null) throw new NoSuchElementException(); + next = null; + return m; + } + }; + } + } +}