package org.simantics.scl.compiler.constants.generic; import gnu.trove.map.hash.THashMap; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.cojen.classfile.TypeDesc; import org.simantics.scl.compiler.constants.generic.MethodRef.ConstructorRef; import org.simantics.scl.compiler.constants.generic.MethodRef.FieldRef; import org.simantics.scl.compiler.constants.generic.MethodRef.ObjectMethodRef; import org.simantics.scl.compiler.constants.generic.MethodRef.SetFieldRef; import org.simantics.scl.compiler.constants.generic.MethodRef.SetStaticFieldRef; import org.simantics.scl.compiler.constants.generic.MethodRef.StaticFieldRef; import org.simantics.scl.compiler.constants.generic.MethodRef.StaticMethodRef; import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase; /** * This class is a reference to a Java class that contains a map of MethodRef for each * method declared by the referenced class. * * Use {@link #getMethodRefs(String)} to get a list of overloaded methods for a given name. */ public class ClassRef { /** * A zero length array used as the value of argument lists of zero-arity methods. */ public static final TypeDesc[] NO_PARAMS = new TypeDesc[0]; /** * A map from method names to lists of identically named methods. */ private THashMap> methods = new THashMap>(); /** * Get a list of methods with a given name. * @param methodName A method name * @return A list of {@link MethodRef} instances that share the name {@code methodName}. */ public List getMethodRefs(String methodName) { List refs = methods.get(methodName); if(refs == null) return Collections.emptyList(); return refs; } /** * Construct a {@link ClassRef} with class loaders and a class name. * @throws ClassNotFoundException if the class is not found. */ public ClassRef(ClassLoader classLoader, String className) throws ClassNotFoundException { analyzeClass(classLoader.loadClass(className), className); } /** * Construct a {@link ClassRef} for a Java class object. */ public ClassRef(Class clazz) { analyzeClass(clazz, MethodBuilderBase.getClassName(clazz)); } /** * Analyse the Java class and store {@link MethodRef} entries for all methods implemented by the class. */ private void analyzeClass(Class clazz, String className) { boolean isInterface = clazz.isInterface(); for(Constructor constructor : clazz.getConstructors()) { addMethodRef("", new ConstructorRef( className, toTypeDescs(constructor.getParameterTypes()) )); } for(Method method : clazz.getMethods()) { if(method.isSynthetic()) continue; String methodName = method.getName(); TypeDesc returnType = TypeDesc.forClass(method.getReturnType()); TypeDesc[] parameters = toTypeDescs(method.getParameterTypes()); int modifiers = method.getModifiers(); if(Modifier.isStatic(modifiers)) addMethodRef(methodName, new StaticMethodRef( className, methodName, returnType, parameters )); else addMethodRef(methodName, new ObjectMethodRef( isInterface, className, methodName, returnType, parameters )); } for(Field field : clazz.getFields()) { String fieldName = field.getName(); TypeDesc type = TypeDesc.forClass(field.getType()); int modifiers = field.getModifiers(); if(Modifier.isStatic(modifiers)) { addMethodRef(fieldName, new StaticFieldRef( className, fieldName, type )); addMethodRef("" + fieldName, new SetStaticFieldRef( className, fieldName, type )); } else { addMethodRef(fieldName, new FieldRef( className, fieldName, type )); addMethodRef("" + fieldName, new SetFieldRef( className, fieldName, type )); } } } /** * Create an array of {@link TypeDesc} instances for an array of Class instances. * For an empty array of classes the result will always be the empty array constant {@link #NO_PARAMS}. */ private static TypeDesc[] toTypeDescs(Class[] classes) { if(classes.length == 0) return NO_PARAMS; TypeDesc[] result = new TypeDesc[classes.length]; for(int i=0;i l = methods.get(name); if(l == null) { l = new ArrayList(2); methods.put(name, l); } l.add(ref); } }