package link.infra.jdwp; import com.sun.jdi.Field; import com.sun.jdi.LocalVariable; import com.sun.jdi.Method; import com.sun.jdi.ReferenceType; import com.sun.jdi.*; import com.sun.jdi.connect.AttachingConnector; import com.sun.jdi.connect.Connector; import com.sun.jdi.connect.IllegalConnectorArgumentsException; import org.apache.bcel.classfile.*; import org.apache.bcel.generic.Type; import org.apache.bcel.generic.*; import java.io.*; import java.util.ArrayList; import java.util.List; import java.util.Map; public class App { public static void main(String[] args) throws IOException, IllegalConnectorArgumentsException { List connectors = Bootstrap.virtualMachineManager().attachingConnectors(); for (AttachingConnector connector : connectors) { if (connector.name().equals("com.sun.jdi.SocketAttach")) { Map connArgs = connector.defaultArguments(); connArgs.get("port").setValue("5005"); VirtualMachine vm = connector.attach(connArgs); for (ReferenceType refType : vm.allClasses()) { if (!(refType instanceof ClassType)) { continue; } ClassType clazz = ((ClassType) refType); // TODO: test with j9+ if (clazz.classLoader() == null) { continue; } // TODO: workaround for lambdas? String sourceName; try { sourceName = clazz.sourceName(); } catch (AbsentInformationException ignored) { continue; } String name = clazz.name().replace('.', '/'); byte[] hmmm = clazz.constantPool(); ByteArrayOutputStream baos = new ByteArrayOutputStream(hmmm.length + 2); DataOutputStream dos = new DataOutputStream(baos); // TODO: unsigned? dos.writeShort(clazz.constantPoolCount()); dos.write(hmmm); ConstantPoolGen poolGen = new ConstantPoolGen(new ConstantPool(new DataInputStream(new ByteArrayInputStream(baos.toByteArray())))); String superClassName; ClassType superClass = clazz.superclass(); if (superClass != null) { superClassName = superClass.name().replace('.', '/'); } else { superClassName = "java/lang/Object"; } String[] implementedInterfaces = clazz.interfaces().stream().map(ReferenceType::name).toArray(String[]::new); ClassGen classGen = new ClassGen(name, superClassName, sourceName, clazz.modifiers(), implementedInterfaces, poolGen); classGen.setMinor(clazz.minorVersion()); classGen.setMajor(clazz.majorVersion()); String classGenericSignature = clazz.genericSignature(); if (classGenericSignature != null && classGenericSignature.length() > 0) { classGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(classGenericSignature), poolGen.getConstantPool())); } // TODO: the necessary metadata for bootstrap methods isn't available!! :( // TODO: inner classes // TODO: enclosing method? for (Field field : clazz.fields()) { FieldGen fieldGen = new FieldGen(field.modifiers(), Type.getType(Utility.getSignature(field.typeName())), field.name(), poolGen); String genericSignature = field.genericSignature(); if (genericSignature != null && genericSignature.length() > 0) { fieldGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(genericSignature), poolGen.getConstantPool())); } classGen.addField(fieldGen.getField()); } for (Method method : clazz.methods()) { String[] argNameList = null; try { argNameList = method.arguments().stream().map(LocalVariable::name).toArray(String[]::new); } catch (AbsentInformationException ignored) {} String[] argTypeList = Utility.methodSignatureArgumentTypes(method.signature()); if (argNameList == null || argTypeList.length != argNameList.length) { // TODO: better names argNameList = argTypeList; } MethodGen methodGen = new MethodGen(method.modifiers(), Type.getReturnType(method.signature()), Type.getArgumentTypes(method.signature()), argNameList, method.name(), name, new InstructionList(method.bytecodes()), poolGen); // TODO: line numbers // TODO: LVT // TODO: LVTT //method.variables(); methodGen.setMaxStack(); methodGen.setMaxLocals(); String genericSignature = method.genericSignature(); if (genericSignature != null && genericSignature.length() > 0) { methodGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(genericSignature), poolGen.getConstantPool())); } classGen.addMethod(methodGen.getMethod()); } if (name.endsWith("class_310")) { classGen.getJavaClass().dump(new File("test.class")); break; } } vm.dispose(); } } } }