package st.gravel.support.jvm.runtime;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import st.gravel.core.Symbol;
import st.gravel.support.compiler.ast.SelectorConverter;
import st.gravel.support.jvm.ArrayExtensions;
public class MethodTools {
private static final SelectorConverter selectorConverter = (SelectorConverter) SelectorConverter.factory
.r_new();
private static final HashMap<Class, Class> primitiveMap = buildPrimitiveMap();
public static java.lang.reflect.Method searchForStaticMethod(Class type,
String name, int numArgs) {
Class primType = primitiveMap.get(type);
if ((primType != null) && (numArgs >= 1)) {
Class[] params = new Class[numArgs];
params[0]= primType;
Method method = searchForMethod(type, name, params, true);
if (method != null ) return method;
}
return searchForMethod(type, name, numArgs, true);
}
private static HashMap<Class, Class> buildPrimitiveMap() {
HashMap<Class, Class> hashMap = new HashMap<Class,Class>();
hashMap.put(Boolean.class, Boolean.TYPE);
hashMap.put(Integer.class, Integer.TYPE );
hashMap.put(Long.class, Long.TYPE );
hashMap.put(Double.class, Double.TYPE );
hashMap.put(Float.class, Float.TYPE );
hashMap.put(Boolean.class, Boolean.TYPE );
hashMap.put(Character.class, Character.TYPE );
hashMap.put(Byte.class, Byte.TYPE );
hashMap.put(Void.class, Void.TYPE );
hashMap.put(Short.class, Short.TYPE );
return hashMap;
}
public static java.lang.reflect.Method searchForMethod(Class type,
String name, int numArgs, boolean isStatic) {
return searchForMethod(type, name, numArgs, isStatic, true);
}
public static java.lang.reflect.Method searchForMethod(Class type,
String name, int numArgs, boolean isStatic, boolean mayCast) {
java.lang.reflect.Method[] methods = type.getMethods();
Method best = null;
for (int i = 0; i < methods.length; i++) {
// Has to be named the same of course.
if (!(methods[i].getName().equals(name)))
continue;
if (!(Modifier.isStatic(methods[i].getModifiers()) == isStatic))
continue;
Class[] types = methods[i].getParameterTypes();
// Does it have the same number of arguments that we're looking for.
if (types.length != numArgs)
continue;
// Check for type compatibility
if (best == null) {
best = methods[i];
} else {
if (mayCast ? areTypesCompatible(methods[i].getParameterTypes(),
best.getParameterTypes()) : areTypesSame(methods[i].getParameterTypes(),
best.getParameterTypes())) {
best = methods[i];
}
}
}
return best;
}
// Following from:
// http://code.google.com/p/tuprolog/source/browse/2p/branches/TRY-ManninoClassLoading/src/alice/util/InspectionUtils.java?spec=svn639&r=639
//
public static java.lang.reflect.Method searchForMethod(Class type,
String name, Class[] parms, boolean isStatic) {
java.lang.reflect.Method[] methods = type.getDeclaredMethods();
Method best = null;
for (int i = 0; i < methods.length; i++) {
// Has to be named the same of course.
Method method = methods[i];
if (!(method.getName().equals(name)))
continue;
if (!(Modifier.isStatic(method.getModifiers()) == isStatic))
continue;
Class[] types = method.getParameterTypes();
// Does it have the same number of arguments that we're looking for.
if (types.length != parms.length)
continue;
// Check for type compatibility
if (areTypesCompatible(types, parms))
if (best == null) {
best = method;
} else {
if (method.getDeclaringClass() != best
.getDeclaringClass()) {
best = method.getDeclaringClass().isAssignableFrom(
best.getDeclaringClass()) ? best : method;
} else {
if ((ArrayExtensions.equals_(types,
best.getParameterTypes()))) {
if (isStatic)
throw new RuntimeException("Weird");
} else {
throw new RuntimeException("TODO: Multiple matches");
}
}
}
}
return best;
}
public static boolean areTypesSame(Class<?>[] targets,
Class<?>[] sources) {
if (targets.length != sources.length)
return (false);
for (int i = 0; i < targets.length; i++) {
if (sources[i] == null)
continue;
if (targets[i].isInterface()) {
Class<?>[] interfaces = sources[i].getInterfaces();
for (Class<?> in : interfaces) {
if (targets[i].equals(in))
return true;
}
}
if (!translateFromPrimitive(targets[i]).equals(
translateFromPrimitive(sources[i])))
return false;
}
return true;
}
public static boolean areTypesCompatible(Class<?>[] targets,
Class<?>[] sources) {
if (targets.length != sources.length)
return (false);
for (int i = 0; i < targets.length; i++) {
if (sources[i] == null)
continue;
if (targets[i].isInterface()) {
Class<?>[] interfaces = sources[i].getInterfaces();
for (Class<?> in : interfaces) {
if (targets[i].equals(in))
return true;
}
}
if (!translateFromPrimitive(targets[i]).isAssignableFrom(
translateFromPrimitive(sources[i])))
return false;
}
return true;
}
public static Class<?> translateFromPrimitive(Class<?> primitive) {
if (!primitive.isPrimitive())
return (primitive);
if (Boolean.TYPE.equals(primitive))
return (Boolean.class);
if (Character.TYPE.equals(primitive))
return (Character.class);
if (Byte.TYPE.equals(primitive))
return (Byte.class);
if (Short.TYPE.equals(primitive))
return (Short.class);
if (Integer.TYPE.equals(primitive))
return (Integer.class);
if (Long.TYPE.equals(primitive))
return (Long.class);
if (Float.TYPE.equals(primitive))
return (Float.class);
if (Double.TYPE.equals(primitive))
return (Double.class);
throw new RuntimeException("Error translating type:" + primitive);
}
public static MethodType asMethodType(Method method) {
return MethodType.methodType(method.getReturnType(),
method.getParameterTypes());
}
public static MethodHandle getHandle(Object receiver, String selectorString) {
return getHandle(receiver, Symbol.value(selectorString));
}
public static MethodHandle getHandle(Object receiver, Symbol selector) {
MethodHandle methodHandle;
String methodName = selectorConverter.selectorAsFunctionName_(selector);
if (receiver == null) {
methodHandle = ImageBootstrapper.systemMapping
.methodHandleForNil_(methodName);
} else {
Class receiverClass = receiver.getClass();
methodHandle = ImageBootstrapper.systemMapping
.methodHandleFor_methodName_(receiverClass, methodName);
}
return methodHandle;
}
public static Object perform(Object receiver, Symbol selector)
throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver);
}
public static Object perform(Object receiver, Symbol selector, Object arg1)
throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver, arg1);
}
public static Object perform(Object receiver, Symbol selector, Object arg1,
Object arg2) throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver, arg1, arg2);
}
public static Object perform(Object receiver, Symbol selector, Object arg1,
Object arg2, Object arg3) throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver, arg1, arg2, arg3);
}
public static Object perform(Object receiver, Symbol selector, Object arg1,
Object arg2, Object arg3, Object arg4) throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver, arg1, arg2, arg3, arg4);
}
public static Object perform_withArguments_(Object receiver,
Symbol selector, Object[] arguments) throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.bindTo(receiver).invokeWithArguments(arguments);
}
public static Object perform(Object receiver, String selector)
throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver);
}
public static Object perform(Object receiver, String selector, Object arg1)
throws Throwable {
MethodHandle handle = getHandle(receiver, selector);
return handle.invoke(receiver, arg1);
}
public static Object safePerform(Object receiver, String selector) {
try {
return perform(receiver, selector);
} catch (Throwable e) {
if (e instanceof RuntimeException) throw (RuntimeException) e;
throw new RuntimeException(e);
}
}
public static Object safePerform(Object receiver, String selector,
Object arg1) {
try {
return perform(receiver, selector, arg1);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
public static void debugTest(String reference, String selector) throws Throwable {
Object testcase = ImageBootstrapper.systemMapping.singletonAtReferenceString_(reference);
MethodTools.perform(testcase, "debug:", (Symbol.value(selector)));
}
}