Если вы реализуете метод
public MethodVisitor visitMethod(
int access, String name, String desc, String signature, String[] exceptions)
у вас уже есть большая часть необходимой информации, которую вы можете собрать в удобочитаемую декларацию. Самым большим препятствием является подпись, но, к счастью, библиотека ASM уже предоставляет инструменты, помогающие в этом.
В следующем коде используются SignatureReader
и TraceSignatureVisitor
для их форматирования. К сожалению, он требует некоторой постобработки, так как он не разделяет параметры типа и параметры метода и опускает Object
возвращаемых типов для необобщенных методов. Кроме того, он генерирует список исключений, только если есть общие исключения, поэтому в противном случае нам придется делать это вручную.
static String decode(int access, String name, String desc, String signature, String[] exceptions) {
if(signature==null) signature=desc;
StringBuilder sb=new StringBuilder();
appendModifiers(sb, access);
TraceSignatureVisitor v = new TraceSignatureVisitor(0);
new SignatureReader(signature).accept(v);
String declaration = v.getDeclaration(), rType = v.getReturnType();
if(declaration.charAt(0)=='<')
sb.append(declaration, 0, declaration.indexOf("(")).append(' ');
else if(rType.isEmpty() || rType.charAt(0)=='[')
sb.append("java.lang.Object");
sb.append(rType).append(' ').append(name)
.append(declaration, declaration.indexOf('('), declaration.length());
if((access&Opcodes.ACC_VARARGS)!=0 && declaration.endsWith("[])"))
sb.replace(sb.length()-3, sb.length(), "...)");
String genericExceptions = v.getExceptions();
if(genericExceptions!=null && !v.getDeclaration().isEmpty())
sb.append(" throws ").append(genericExceptions);
else if(exceptions!=null && exceptions.length>0) {
sb.append(" throws ");
int pos=sb.length();
for(String e: exceptions) sb.append(e).append(", ");
int e=sb.length()-2;
sb.setLength(e);
for(; pos<e; pos++) if(sb.charAt(pos)=='/') sb.setCharAt(pos, '.');
}
return sb.toString();
}
private static void appendModifiers(StringBuilder buf, int access) {
for(int bit; access!=0; access-=bit) {
bit=access & -access;
switch(bit) {
case Opcodes.ACC_PUBLIC: buf.append("public "); break;
case Opcodes.ACC_PRIVATE: buf.append("private "); break;
case Opcodes.ACC_PROTECTED: buf.append("protected "); break;
case Opcodes.ACC_STATIC: buf.append("static "); break;
case Opcodes.ACC_FINAL: buf.append("final "); break;
case Opcodes.ACC_ABSTRACT: buf.append("abstract "); break;
case Opcodes.ACC_NATIVE: buf.append("native "); break;
case Opcodes.ACC_STRICT: buf.append("strictfp "); break;
case Opcodes.ACC_SYNCHRONIZED: buf.append("synchronized "); break;
}
}
}
Он также декодирует модификаторы вручную, как вы можете видеть в конце, но это не такая уж большая проблема.
Если вы протестируете его с помощью:
String[] exceptions={"java/lang/Exception"};
System.out.println(decode(9, "testMethod", "(Ljava/lang/Object;)Ljava/lang/Object;",
"<T:Ljava/lang/Object;>(TT;)TT;", exceptions));
он будет печатать:
public static <T> T testMethod(T) throws java.lang.Exception
Когда дело доходит до разбора такого объявления, все становится сложнее. Поскольку вы собираетесь реализовать редактор, а не ассемблер, вы можете подумать об альтернативе предоставления отдельных компонентов редактора для различных функций, например. флажки и поля со списком для модификаторов, текстовое поле для имени и редактор списка для типов параметров.
На этом этапе я бы рекомендовал изучить Type
< /а> класс. Это позволяет извлекать типы параметров и возвращает тип из подписи, и после применения изменений вы можете восстановить подпись.
Точно так же SignatureReader
и SignatureWriter
может помочь справиться с общей подписью в структурным способом, а не преобразовывать их в текстовую форму и повторно анализировать эту форму обратно.
person
Holger
schedule
09.10.2015