/*
* Copyright 2003-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.runtime.metaclass;
import groovy.lang.MetaMethod;
import org.codehaus.groovy.reflection.CachedClass;
import org.codehaus.groovy.util.FastArray;
import org.codehaus.groovy.reflection.GeneratedMetaMethod;
import org.codehaus.groovy.util.SingleKeyHashMap;
import java.util.NoSuchElementException;
public class MetaMethodIndex {
public SingleKeyHashMap methodHeaders = new SingleKeyHashMap();
public static class Header {
public Entry head;
Class cls;
public int clsHashCode31;
public Class subclass;
public Header(Class cls) {
this (cls, null);
}
public Header(Class cls, Class subclass) {
this.cls = cls;
this.subclass = subclass;
this.clsHashCode31 = 31 * cls.hashCode();
}
}
public static class CacheEntry {
public Class [] params;
public MetaMethod method;
}
public static class Entry {
public int hash;
public Entry nextHashEntry, nextClassEntry;
public String name;
public Class cls;
public Object methods, methodsForSuper, staticMethods;
public CacheEntry cachedMethod, cachedMethodForSuper, cachedStaticMethod;
public String toString () {
return "[" + name + ", " + cls.getName() + "]";
}
}
public MetaMethodIndex(CachedClass theCachedClass) {
init(DEFAULT_CAPACITY);
CachedClass last = null;
if (!theCachedClass.isInterface()) {
for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
final SingleKeyHashMap.Entry e = methodHeaders.getOrPut(c.getTheClass());
e.value = new Header (c.getTheClass(), last == null ? null : last.getTheClass());
last = c;
}
}
else {
final SingleKeyHashMap.Entry e = methodHeaders.getOrPut(Object.class);
e.value = new Header (Object.class, theCachedClass.getTheClass());
}
}
protected Entry table[];
protected static final int DEFAULT_CAPACITY = 32;
protected static final int MINIMUM_CAPACITY = 4;
protected static final int MAXIMUM_CAPACITY = 1 << 28;
protected int size;
protected transient int threshold;
public static int hash(int h) {
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
Object[] tab = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
public void init(int initCapacity) {
threshold = (initCapacity * 6) / 8;
table = new Entry[initCapacity];
}
public void resize(int newLength) {
Entry[] oldTable = table;
int oldLength = table.length;
Entry[] newTable = new Entry[newLength];
for (int j = 0; j < oldLength; j++) {
for (Entry e = oldTable[j]; e != null;) {
Entry next = e.nextHashEntry;
int index = e.hash & (newLength - 1);
e.nextHashEntry = newTable[index];
newTable[index] = e;
e = next;
}
}
table = newTable;
threshold = (6 * newLength) / 8;
}
public interface EntryIterator {
boolean hasNext();
Entry next();
}
public Entry[] getTable() {
return table;
}
public EntryIterator getEntrySetIterator() {
return new EntryIterator() {
Entry next; // next entry to return
int index; // current slot
Entry current; // current entry
{
Entry[] t = table;
int i = t.length;
Entry n = null;
if (size != 0) { // advance to first entry
while (i > 0 && (n = t[--i]) == null) {
}
}
next = n;
index = i;
}
public boolean hasNext() {
return next != null;
}
public Entry next() {
return nextEntry();
}
Entry nextEntry() {
Entry e = next;
if (e == null)
throw new NoSuchElementException();
Entry n = e.nextHashEntry;
Entry[] t = table;
int i = index;
while (n == null && i > 0)
n = t[--i];
index = i;
next = n;
return current = e;
}
};
}
public final Entry getMethods(Class cls, String name) {
int h = hash(31 * cls.hashCode() + name.hashCode());
Entry e = table[h & (table.length - 1)];
for (; e != null; e = e.nextHashEntry)
if (e.hash == h && cls == e.cls && (e.name == name || e.name.equals(name)) )
return e;
return null;
}
public Entry getOrPutMethods(String name, Header header) {
final Class cls = header.cls;
int h = hash(header.clsHashCode31 + name.hashCode());
final Entry[] t = table;
final int index = h & (t.length - 1);
Entry e = t[index];
for (; e != null; e = e.nextHashEntry)
if (e.hash == h && cls == e.cls && (e.name == name || e.name.equals(name)) )
return e;
Entry entry = new Entry();
entry.nextHashEntry = t[index];
entry.hash = h;
entry.name = name.intern();
entry.cls = cls;
t[index] = entry;
entry.nextClassEntry = header.head;
header.head = entry;
if (++size == threshold)
resize(2 * t.length);
return entry;
}
public Header getHeader(Class cls) {
Header header;
final SingleKeyHashMap.Entry head = methodHeaders.getOrPut(cls);
if (head.value == null) {
head.value = new Header(cls);
}
header = (Header) head.value;
return header;
}
public void copyNonPrivateMethods(Class from, Class to) {
copyNonPrivateMethods(getHeader(from), getHeader(to));
}
public void copyNonPrivateMethods(Header from, Header to) {
for (Entry e = from.head; e != null; e = e.nextClassEntry)
copyNonPrivateMethods(e, to);
}
public void copyAllMethodsToSuper(Header from, Header to) {
for (Entry e = from.head; e != null; e = e.nextClassEntry)
copyAllMethodsToSuper(e, to);
}
public void copyNonPrivateMethodsFromSuper(Header from) {
for (Entry e = from.head; e != null; e = e.nextClassEntry)
copyNonPrivateMethodsFromSuper(e);
}
private void copyNonPrivateMethods(Entry from, Header to) {
Object oldListOrMethod = from.methods;
if (oldListOrMethod instanceof FastArray) {
FastArray oldList = (FastArray) oldListOrMethod;
Entry e = null;
int len1 = oldList.size();
Object list[] = oldList.getArray();
for (int j = 0; j != len1; ++j) {
MetaMethod method = (MetaMethod) list[j];
if (method.isPrivate()) continue;
if (e == null)
e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
} else {
MetaMethod method = (MetaMethod) oldListOrMethod;
if (!method.isPrivate()) {
Entry e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
}
}
private void copyAllMethodsToSuper(Entry from, Header to) {
Object oldListOrMethod = from.methods;
if (oldListOrMethod instanceof FastArray) {
FastArray oldList = (FastArray) oldListOrMethod;
Entry e = null;
int len1 = oldList.size();
Object list[] = oldList.getArray();
for (int j = 0; j != len1; ++j) {
MetaMethod method = (MetaMethod) list[j];
if (e == null)
e = getOrPutMethods(from.name, to);
e.methodsForSuper = addMethodToList(e.methodsForSuper, method);
}
} else {
MetaMethod method = (MetaMethod) oldListOrMethod;
Entry e = getOrPutMethods(from.name, to);
e.methodsForSuper = addMethodToList(e.methodsForSuper, method);
}
}
private void copyNonPrivateMethodsFromSuper(Entry e) {
Object oldListOrMethod = e.methodsForSuper;
if (oldListOrMethod == null)
return;
if (oldListOrMethod instanceof FastArray) {
FastArray oldList = (FastArray) oldListOrMethod;
int len1 = oldList.size();
Object list[] = oldList.getArray();
for (int j = 0; j != len1; ++j) {
MetaMethod method = (MetaMethod) list[j];
if (method.isPrivate()) continue;
e.methods = addMethodToList(e.methods, method);
}
} else {
MetaMethod method = (MetaMethod) oldListOrMethod;
if (!method.isPrivate()) {
e.methods = addMethodToList(e.methods, method);
}
}
}
public void copyNonPrivateMethodsDown(Class from, Class to) {
copyNonPrivateNonNewMetaMethods(getHeader(from), getHeader(to));
}
public void copyNonPrivateNonNewMetaMethods(Header from, Header to) {
for (Entry e = from.head; e != null; e = e.nextClassEntry)
copyNonPrivateNonNewMetaMethods(e, to);
}
private void copyNonPrivateNonNewMetaMethods(Entry from, Header to) {
Object oldListOrMethod = from.methods;
if (oldListOrMethod == null)
return;
if (oldListOrMethod instanceof FastArray) {
FastArray oldList = (FastArray) oldListOrMethod;
Entry e = null;
int len1 = oldList.size();
Object list[] = oldList.getArray();
for (int j = 0; j != len1; ++j) {
MetaMethod method = (MetaMethod) list[j];
if (method instanceof NewMetaMethod || method.isPrivate()) continue;
if (e == null)
e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
} else {
MetaMethod method = (MetaMethod) oldListOrMethod;
if (method instanceof NewMetaMethod || method.isPrivate()) return;
Entry e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
}
public Object addMethodToList(Object o, MetaMethod method) {
if (o == null) {
return method;
}
if (o instanceof MetaMethod) {
MetaMethod match = (MetaMethod) o;
if (!isMatchingMethod(match, method)) {
FastArray list = new FastArray(2);
list.add(match);
list.add(method);
return list;
} else {
if (match.isPrivate()
|| (!isNonRealMethod(match) && match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface())) {
// do not overwrite interface methods with instance methods
// do not overwrite private methods
// Note: private methods from parent classes are not shown here,
// but when doing the multimethod connection step, we overwrite
// methods of the parent class with methods of a subclass and
// in that case we want to keep the private methods
} else {
CachedClass methodC = method.getDeclaringClass();
CachedClass matchC = match.getDeclaringClass();
if (methodC == matchC) {
if (isNonRealMethod(method)) {
return method;
}
} else if (!methodC.isAssignableFrom(matchC.getTheClass())) {
return method;
}
}
}
return o;
}
if (o instanceof FastArray) {
FastArray list = (FastArray) o;
int found = findMatchingMethod(list, method);
if (found == -1) {
list.add(method);
} else {
MetaMethod match = (MetaMethod) list.get(found);
if (match==method) return o;
if (match.isPrivate()
|| (!isNonRealMethod(match) && match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface())) {
// do not overwrite interface methods with instance methods
// do not overwrite private methods
// Note: private methods from parent classes are not shown here,
// but when doing the multimethod connection step, we overwrite
// methods of the parent class with methods of a subclass and
// in that case we want to keep the private methods
} else {
CachedClass methodC = method.getDeclaringClass();
CachedClass matchC = match.getDeclaringClass();
if (methodC == matchC) {
if (isNonRealMethod(method)) {
list.set(found, method);
}
} else if (!methodC.isAssignableFrom(matchC.getTheClass())) {
list.set(found, method);
}
}
}
}
return o;
}
private boolean isNonRealMethod(MetaMethod method) {
return method instanceof NewInstanceMetaMethod ||
method instanceof NewStaticMetaMethod ||
method instanceof ClosureMetaMethod ||
method instanceof GeneratedMetaMethod ||
method instanceof ClosureStaticMetaMethod ||
method instanceof MixinInstanceMetaMethod ||
method instanceof ClosureMetaMethod.AnonymousMetaMethod;
}
private boolean isMatchingMethod(MetaMethod aMethod, MetaMethod method) {
if (aMethod==method) return true;
CachedClass[] params1 = aMethod.getParameterTypes();
CachedClass[] params2 = method.getParameterTypes();
if (params1.length != params2.length) {
return false;
}
boolean matches = true;
for (int i = 0; i < params1.length; i++) {
if (params1[i] != params2[i]) {
matches = false;
break;
}
}
return matches;
}
private int findMatchingMethod(FastArray list, MetaMethod method) {
int len = list.size();
Object data[] = list.getArray();
for (int j = 0; j != len; ++j) {
MetaMethod aMethod = (MetaMethod) data[j];
if (isMatchingMethod(aMethod, method))
return j;
}
return -1;
}
public void copyMethodsToSuper() {
Entry[] table = this.table;
int length = table.length;
for (int j = 0; j < length; j++) {
for (Entry e = table[j]; e != null; e = e.nextHashEntry) {
if (e.methods instanceof FastArray)
e.methodsForSuper = ((FastArray) e.methods).copy();
else
e.methodsForSuper = e.methods;
}
}
}
public void copy(Class c, Header index) {
copy(getHeader(c), index);
}
public void copy(Header from, Header to) {
for (Entry e = from.head; e != null; e = e.nextClassEntry)
copyAllMethods(e, to);
}
private void copyAllMethods(Entry from, Header to) {
Object oldListOrMethod = from.methods;
if (oldListOrMethod instanceof FastArray) {
FastArray oldList = (FastArray) oldListOrMethod;
Entry e = null;
int len1 = oldList.size();
Object list[] = oldList.getArray();
for (int j = 0; j != len1; ++j) {
MetaMethod method = (MetaMethod) list[j];
if (e == null)
e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
} else {
MetaMethod method = (MetaMethod) oldListOrMethod;
if (!method.isPrivate()) {
Entry e = getOrPutMethods(from.name, to);
e.methods = addMethodToList(e.methods, method);
}
}
}
public void clearCaches() {
for (int i = 0; i != table.length; ++i )
for (Entry e = table [i]; e != null; e = e.nextHashEntry ) {
e.cachedMethod = e.cachedMethodForSuper = e.cachedStaticMethod = null;
}
}
public void clearCaches(String name) {
for (int i = 0; i != table.length; ++i )
for (Entry e = table [i]; e != null; e = e.nextHashEntry ) {
if (e.name.equals(name)) {
e.cachedMethod = e.cachedMethodForSuper = e.cachedStaticMethod = null;
}
}
}
}