@Override public Object asConstantPoolValue() { StringBuilder stringBuilder = new StringBuilder("("); for (TypeDescription parameterType : getParameterTypes()) { stringBuilder.append(parameterType.getDescriptor()); } return Type.getMethodType( stringBuilder.append(")").append(getReturnType().getDescriptor()).toString()); }
@Override void write(MethodWriter writer, Globals globals) { writer.writeDebugInfo(location); if (defPointer != null) { // dynamic interface: push captured parameter on stack // TODO: don't do this: its just to cutover :) writer.push((String) null); writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot()); } else if (ref == null) { // typed interface, dynamic implementation writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot()); Type methodType = Type.getMethodType(expected.type, captured.type.type); writer.invokeDefCall(call, methodType, DefBootstrap.REFERENCE, expected.name); } else { // typed interface, typed implementation writer.visitVarInsn(captured.type.type.getOpcode(Opcodes.ILOAD), captured.getSlot()); // convert MethodTypes to asm Type for the constant pool. String invokedType = ref.invokedType.toMethodDescriptorString(); Type samMethodType = Type.getMethodType(ref.samMethodType.toMethodDescriptorString()); Type interfaceType = Type.getMethodType(ref.interfaceMethodType.toMethodDescriptorString()); if (ref.needsBridges()) { writer.invokeDynamic( ref.invokedName, invokedType, LAMBDA_BOOTSTRAP_HANDLE, samMethodType, ref.implMethodASM, samMethodType, LambdaMetafactory.FLAG_BRIDGES, 1, interfaceType); } else { writer.invokeDynamic( ref.invokedName, invokedType, LAMBDA_BOOTSTRAP_HANDLE, samMethodType, ref.implMethodASM, samMethodType, 0); } } }
/** * Transforms the given method type into a stack manipulation that loads its type onto the operand * stack. * * @param methodType The method type that should be loaded onto the operand stack. * @return A stack manipulation that loads the given method type. */ public static StackManipulation of(JavaInstance.MethodType methodType) { Type[] parameterType = new Type[methodType.getParameterTypes().size()]; int index = 0; for (TypeDescription typeDescription : methodType.getParameterTypes()) { parameterType[index++] = Type.getType(typeDescription.getDescriptor()); } return new MethodTypeConstant( Type.getMethodType( Type.getType(methodType.getReturnType().getDescriptor()), parameterType)); }
private GaMethod toMethod( ClassLoader loader, Class<?> clazz, String methodName, String methodDesc) throws ClassNotFoundException, NoSuchMethodException { final org.objectweb.asm.Type asmType = org.objectweb.asm.Type.getMethodType(methodDesc); // to arg types final Class<?>[] argsClasses = new Class<?>[asmType.getArgumentTypes().length]; for (int index = 0; index < argsClasses.length; index++) { // asm class descriptor to jvm class final Class<?> argumentClass; final Type argumentAsmType = asmType.getArgumentTypes()[index]; switch (argumentAsmType.getSort()) { case Type.BOOLEAN: { argumentClass = boolean.class; break; } case Type.CHAR: { argumentClass = char.class; break; } case Type.BYTE: { argumentClass = byte.class; break; } case Type.SHORT: { argumentClass = short.class; break; } case Type.INT: { argumentClass = int.class; break; } case Type.FLOAT: { argumentClass = float.class; break; } case Type.LONG: { argumentClass = long.class; break; } case Type.DOUBLE: { argumentClass = double.class; break; } case Type.ARRAY: { argumentClass = toClass(loader, argumentAsmType.getInternalName()); break; } case Type.VOID: { argumentClass = void.class; break; } case Type.OBJECT: case Type.METHOD: default: { argumentClass = toClass(loader, argumentAsmType.getClassName()); break; } } argsClasses[index] = argumentClass; } // to method or constructor if (GaCheckUtils.isEquals(methodName, "<init>")) { return GaMethod.newInit(toConstructor(clazz, argsClasses)); } else { return GaMethod.newMethod(toMethod(clazz, methodName, argsClasses)); } }
/** * Finds any class with a {@code public static main} method by performing a breadth first search. * * @author Phillip Webb */ public abstract class MainClassFinder { private static final String DOT_CLASS = ".class"; private static final Type STRING_ARRAY_TYPE = Type.getType(String[].class); private static final Type MAIN_METHOD_TYPE = Type.getMethodType(Type.VOID_TYPE, STRING_ARRAY_TYPE); private static final String MAIN_METHOD_NAME = "main"; private static final FileFilter CLASS_FILE_FILTER = new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(DOT_CLASS)); } }; private static final FileFilter PACKAGE_FOLDER_FILTER = new FileFilter() { @Override public boolean accept(File file) { return file.isDirectory() && !file.getName().startsWith("."); } }; /** * Find the main class from a given folder. * * @param rootFolder the root folder to search * @return the main class or {@code null} * @throws IOException */ public static String findMainClass(File rootFolder) throws IOException { if (!rootFolder.isDirectory()) { throw new IllegalArgumentException("Inavlid root folder '" + rootFolder + "'"); } File mainClassFile = findMainClassFile(rootFolder); if (mainClassFile == null) { return null; } String mainClass = mainClassFile.getAbsolutePath(); return convertToClassName(mainClass, rootFolder.getAbsolutePath() + "/"); } private static File findMainClassFile(File root) throws IOException { Deque<File> stack = new ArrayDeque<File>(); stack.push(root); while (!stack.isEmpty()) { File file = stack.pop(); if (file.isFile()) { InputStream inputStream = new FileInputStream(file); try { if (isMainClass(inputStream)) { return file; } } finally { inputStream.close(); } } if (file.isDirectory()) { pushAllSorted(stack, file.listFiles(PACKAGE_FOLDER_FILTER)); pushAllSorted(stack, file.listFiles(CLASS_FILE_FILTER)); } } return null; } private static void pushAllSorted(Deque<File> stack, File[] files) { Arrays.sort( files, new Comparator<File>() { @Override public int compare(File o1, File o2) { return o1.getName().compareTo(o2.getName()); } }); for (File file : files) { stack.push(file); } } /** * Find the main class in a given jar file. * * @param jarFile the jar file to search * @param classesLocation the location within the jar containing classes * @return the main class or {@code null} * @throws IOException */ public static String findMainClass(JarFile jarFile, String classesLocation) throws IOException { List<JarEntry> classEntries = getClassEntries(jarFile, classesLocation); Collections.sort(classEntries, new ClassEntryComparator()); for (JarEntry entry : classEntries) { InputStream inputStream = new BufferedInputStream(jarFile.getInputStream(entry)); try { if (isMainClass(inputStream)) { String name = entry.getName(); name = convertToClassName(name, classesLocation); return name; } } finally { inputStream.close(); } } return null; } private static String convertToClassName(String name, String prefix) { name = name.replace("/", "."); name = name.replace('\\', '.'); name = name.substring(0, name.length() - DOT_CLASS.length()); if (prefix != null) { name = name.substring(prefix.length()); } return name; } private static List<JarEntry> getClassEntries(JarFile source, String classesLocation) { classesLocation = (classesLocation != null ? classesLocation : ""); Enumeration<JarEntry> sourceEntries = source.entries(); List<JarEntry> classEntries = new ArrayList<JarEntry>(); while (sourceEntries.hasMoreElements()) { JarEntry entry = sourceEntries.nextElement(); if (entry.getName().startsWith(classesLocation) && entry.getName().endsWith(DOT_CLASS)) { classEntries.add(entry); } } return classEntries; } private static boolean isMainClass(InputStream inputStream) { try { ClassReader classReader = new ClassReader(inputStream); MainMethodFinder mainMethodFinder = new MainMethodFinder(); classReader.accept(mainMethodFinder, ClassReader.SKIP_CODE); return mainMethodFinder.isFound(); } catch (IOException ex) { return false; } } private static class ClassEntryComparator implements Comparator<JarEntry> { @Override public int compare(JarEntry o1, JarEntry o2) { Integer d1 = getDepth(o1); Integer d2 = getDepth(o2); int depthCompare = d1.compareTo(d2); if (depthCompare != 0) { return depthCompare; } return o1.getName().compareTo(o2.getName()); } private int getDepth(JarEntry entry) { return entry.getName().split("/").length; } } private static class MainMethodFinder extends ClassVisitor { private boolean found; public MainMethodFinder() { super(Opcodes.ASM4); } @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { if (isAccess(access, Opcodes.ACC_PUBLIC, Opcodes.ACC_STATIC) && MAIN_METHOD_NAME.equals(name) && MAIN_METHOD_TYPE.getDescriptor().equals(desc)) { this.found = true; } return null; } private boolean isAccess(int access, int... requiredOpsCodes) { for (int requiredOpsCode : requiredOpsCodes) { if ((access & requiredOpsCode) == 0) { return false; } } return true; } public boolean isFound() { return this.found; } } }
/** * Transforms the given method into a stack manipulation that loads its type onto the operand * stack. * * @param methodDescription The method of which the method type should be loaded onto the operand * stack. * @return A stack manipulation that loads the method type of the given method onto the operand * stack. */ public static StackManipulation of(MethodDescription methodDescription) { return new MethodTypeConstant(Type.getMethodType(methodDescription.getDescriptor())); }