package org.simpleflatmapper.reflect.asm; import org.simpleflatmapper.ow2asm.ClassReader; import org.simpleflatmapper.ow2asm.ClassVisitor; import org.simpleflatmapper.ow2asm.Label; import org.simpleflatmapper.ow2asm.MethodVisitor; import org.simpleflatmapper.ow2asm.Opcodes; import org.simpleflatmapper.reflect.instantiator.ExecutableInstantiatorDefinition; import org.simpleflatmapper.reflect.InstantiatorDefinition; import org.simpleflatmapper.reflect.Parameter; import org.simpleflatmapper.util.ErrorHelper; import org.simpleflatmapper.util.TypeHelper; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Member; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class AsmInstantiatorDefinitionFactory { public static List<InstantiatorDefinition> extractDefinitions(final Type target) throws IOException { final List<InstantiatorDefinition> constructors = new ArrayList<InstantiatorDefinition>(); final Class<?> targetClass = TypeHelper.toClass(target); ClassLoader cl = targetClass.getClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } final String fileName = targetClass.getName().replace('.', '/') + ".class"; final InputStream is = cl.getResourceAsStream(fileName); try { if (is == null) { throw new IOException("Cannot find file " + fileName + " in " + cl); } ClassReader classReader = new ClassReader(is); classReader.accept(new ClassVisitor(Opcodes.ASM5) { List<String> genericTypeNames; @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { if (signature != null) { genericTypeNames = AsmUtils.extractGenericTypeNames(signature); } else { genericTypeNames = Collections.emptyList(); } super.visit(version, access, name, signature, superName, interfaces); } @Override public MethodVisitor visitMethod(int access, final String methodName, String desc, String signature, String[] exceptions) { final boolean isConstructor = "<init>".equals(methodName); if ((Opcodes.ACC_PUBLIC & access) == Opcodes.ACC_PUBLIC && (isConstructor || ((Opcodes.ACC_STATIC & access) == Opcodes.ACC_STATIC && !desc.endsWith("V")))) { final List<String> descTypes = AsmUtils.extractTypeNames(desc); final List<String> genericTypes; final List<String> names = new ArrayList<String>(); if (signature != null) { genericTypes = AsmUtils.extractTypeNames(signature); } else { genericTypes = descTypes; } if (!isConstructor) { if (descTypes.size() > 0) { try { final Type genericType = AsmUtils.toGenericType(descTypes.get(descTypes.size() - 1), genericTypeNames, target); if (!targetClass.isAssignableFrom(TypeHelper.toClass(genericType))) { return null; } } catch (ClassNotFoundException e) { return null; } } else return null; } return new MethodVisitor(Opcodes.ASM5) { Label firstLabel; Label lastLabel; @Override public void visitLabel(Label label) { if (firstLabel == null) { firstLabel = label; } lastLabel = label; } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { if (start.equals(firstLabel) && end.equals(lastLabel) && ! "this".equals(name)) { names.add(name); } } @Override public void visitEnd() { try { final List<Parameter> parameters = new ArrayList<Parameter>(); int l = descTypes.size() - (isConstructor ? 0 : 1); for(int i = 0; i < l; i ++) { String name = "arg" + i; if (i < names.size()) { name =names.get(i); } parameters.add(createParameter(i, name, descTypes.get(i), genericTypes.get(i))); } final Member executable; if (isConstructor) { executable = targetClass.getDeclaredConstructor(toTypeArray(parameters)); } else { executable = targetClass.getDeclaredMethod(methodName, toTypeArray(parameters)); } constructors.add(new ExecutableInstantiatorDefinition(executable, parameters.toArray(new Parameter[0]))); } catch(Exception e) { ErrorHelper.rethrow(e); } } private Class<?>[] toTypeArray(List<Parameter> parameters) { Class<?>[] types = new Class<?>[parameters.size()]; for(int i = 0; i < types.length; i++) { types[i] = parameters.get(i).getType(); } return types; } private Parameter createParameter(int index, String name, String desc, String signature) { try { Type basicType = AsmUtils.toGenericType(desc, genericTypeNames, target); Type genericType = basicType; if (signature != null) { Type type = AsmUtils.toGenericType(signature, genericTypeNames, target); if (type != null) { genericType = type; } } return new Parameter(index, name, TypeHelper.toClass(basicType), genericType); } catch (ClassNotFoundException e) { throw new Error("Unexpected error " + e, e); } } }; } else { return null; } } }, 0); } finally { if (is != null) { try { is.close(); } catch (Exception e) { } } } return constructors; } }