gpt4 book ai didi

java - pretty-print Java 方法签名并解析回来

转载 作者:行者123 更新时间:2023-12-01 11:05:19 25 4
gpt4 key购买 nike

我正在制作一个 Java 字节码编辑器,它将字节码转换为某种可由用户更改的中间代码,然后将该代码解析回字节码。目前,方法声明看起来很糟糕且难以阅读。但是,好吧,它确实有效。

method [public,static] testMethod (Ljava/lang/Object;)Ljava/lang/Object; throws [java/lang/Exception] <T:Ljava/lang/Object;>(TT;)TT; 

如您所见,我从

获得了访问标志、方法名称、方法描述、泛型签名和异常
org.objectweb.asm.ClassVisitor#visitMethod

是否有一种方便的方法使其看起来像原始源代码,然后从该漂亮的字符串中提取所有信息?上面的方法应该是这样的:

public static <T> T testMethod(T) throws Exception

谢谢!

最佳答案

如果您实现method

public MethodVisitor visitMethod(
int access, String name, String desc, String signature, String[] exceptions)

您已经拥有了大部分所需的信息,您可以将这些信息组装成人类可读的声明。最大的障碍是签名,但幸运的是,ASM 库已经提供了有助于此目的的工具。

以下代码使用 SignatureReaderTraceSignatureVisitor来格式化它们。不幸的是,它需要一些后处理,因为它没有分离类型参数和方法参数,并且省略了非泛型方法的 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;
buf.append(switch(bit) {
case Opcodes.ACC_PUBLIC -> "public ";
case Opcodes.ACC_PRIVATE -> "private ";
case Opcodes.ACC_PROTECTED -> "protected ";
case Opcodes.ACC_STATIC -> "static ";
case Opcodes.ACC_FINAL -> "final ";
case Opcodes.ACC_ABSTRACT -> "abstract ";
case Opcodes.ACC_NATIVE -> "native ";
case Opcodes.ACC_SYNCHRONIZED -> "synchronized ";
default -> "";
});
}
}

appendModifiers 方法已针对最近的 Java 版本进行了调整,对于旧版本,请查看 here

它还会手动解码修饰符,正如您在最后看到的那样,但这没什么大不了的。

如果你测试它:

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类(class)。它允许提取 parameter typesreturn type从签名并应用更改后,您可以 recreate a signature .

同样,SignatureReaderSignatureWriter可能有助于以结构化方式处理通用签名,而不是将它们转换为文本形式并重新解析该形式。

关于java - pretty-print Java 方法签名并解析回来,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33025409/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com