ASMifier 显示不够
ASMifier doesn't display enough
我正在尝试使用 ASMifier 来准确理解我的 .class 文件中的内容。问题是该工具省略了某些 mv.visitXXX。例如,它不会为行声明标签显示 mv.visitLabel(Label)。
这是有道理的,因为 ASMifier 仅显示创建 class 所需的 mv.visit,但我想以不同的方式使用它。
是否可以要求该工具显示它访问的所有内容?
在ASMifier
中没有条件处理与源代码行关联的标签,换句话说,如果ClassReader
报告它们,ASMifier
将显示它们。
要使其工作,必须满足两个条件:
该方法必须有一个 LineNumberTable
属性与其 Code
属性关联(对于普通 类,这是由生成.class
文件).
您不能在 ClassReader
的 accept
方法中指定 SKIP_DEBUG
使用javac
时,调试信息的存在与否由-g
选项控制。默认是生成 LineNumberTable
属性,所以不指定 -g
选项已经足够了。所以下面的程序
package bytecodetests;
import java.io.IOException;
import java.io.PrintWriter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.TraceClassVisitor;
public class BytecodeTests {
public static void main(String[] args) throws IOException {
new ClassReader(BytecodeTests.class.getResourceAsStream("BytecodeTests.class"))
.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(System.out)), 0);
}
}
会产生
package asm.bytecodetests;
import java.util.*;
import org.objectweb.asm.*;
public class BytecodeTestsDump implements Opcodes {
public static byte[] dump () throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "bytecodetests/BytecodeTests", null, "java/lang/Object", null);
cw.visitSource("BytecodeTests.java", null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(9, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "Lbytecodetests/BytecodeTests;", null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, new String[] { "java/io/IOException" });
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(12, l0);
mv.visitTypeInsn(NEW, "org/objectweb/asm/ClassReader");
mv.visitInsn(DUP);
mv.visitLdcInsn(Type.getType("Lbytecodetests/BytecodeTests;"));
mv.visitLdcInsn("BytecodeTests.class");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/ClassReader", "<init>", "(Ljava/io/InputStream;)V", false);
mv.visitTypeInsn(NEW, "org/objectweb/asm/util/TraceClassVisitor");
mv.visitInsn(DUP);
mv.visitInsn(ACONST_NULL);
mv.visitTypeInsn(NEW, "org/objectweb/asm/util/ASMifier");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/util/ASMifier", "<init>", "()V", false);
mv.visitTypeInsn(NEW, "java/io/PrintWriter");
mv.visitInsn(DUP);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitMethodInsn(INVOKESPECIAL, "java/io/PrintWriter", "<init>", "(Ljava/io/OutputStream;)V", false);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/util/TraceClassVisitor", "<init>", "(Lorg/objectweb/asm/ClassVisitor;Lorg/objectweb/asm/util/Printer;Ljava/io/PrintWriter;)V", false);
mv.visitInsn(ICONST_0);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(13, l1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/objectweb/asm/ClassReader", "accept", "(Lorg/objectweb/asm/ClassVisitor;I)V", false);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLineNumber(14, l2);
mv.visitInsn(RETURN);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l3, 0);
mv.visitMaxs(8, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
注意
的存在
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(12, l0);
…
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(13, l1);
…
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLineNumber(14, l2);
…
调用 main
方法。
当从命令行 运行 ASMifier
时,它确实默认使用 SKIP_DEBUG
标志,除非 -debug
选项已作为第一个参数给出。
我正在尝试使用 ASMifier 来准确理解我的 .class 文件中的内容。问题是该工具省略了某些 mv.visitXXX。例如,它不会为行声明标签显示 mv.visitLabel(Label)。
这是有道理的,因为 ASMifier 仅显示创建 class 所需的 mv.visit,但我想以不同的方式使用它。
是否可以要求该工具显示它访问的所有内容?
在ASMifier
中没有条件处理与源代码行关联的标签,换句话说,如果ClassReader
报告它们,ASMifier
将显示它们。
要使其工作,必须满足两个条件:
该方法必须有一个
LineNumberTable
属性与其Code
属性关联(对于普通 类,这是由生成.class
文件).您不能在
ClassReader
的
accept
方法中指定 SKIP_DEBUG
使用javac
时,调试信息的存在与否由-g
选项控制。默认是生成 LineNumberTable
属性,所以不指定 -g
选项已经足够了。所以下面的程序
package bytecodetests;
import java.io.IOException;
import java.io.PrintWriter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.TraceClassVisitor;
public class BytecodeTests {
public static void main(String[] args) throws IOException {
new ClassReader(BytecodeTests.class.getResourceAsStream("BytecodeTests.class"))
.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(System.out)), 0);
}
}
会产生
package asm.bytecodetests;
import java.util.*;
import org.objectweb.asm.*;
public class BytecodeTestsDump implements Opcodes {
public static byte[] dump () throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(52, ACC_PUBLIC + ACC_SUPER, "bytecodetests/BytecodeTests", null, "java/lang/Object", null);
cw.visitSource("BytecodeTests.java", null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(9, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "Lbytecodetests/BytecodeTests;", null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, new String[] { "java/io/IOException" });
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(12, l0);
mv.visitTypeInsn(NEW, "org/objectweb/asm/ClassReader");
mv.visitInsn(DUP);
mv.visitLdcInsn(Type.getType("Lbytecodetests/BytecodeTests;"));
mv.visitLdcInsn("BytecodeTests.class");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getResourceAsStream", "(Ljava/lang/String;)Ljava/io/InputStream;", false);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/ClassReader", "<init>", "(Ljava/io/InputStream;)V", false);
mv.visitTypeInsn(NEW, "org/objectweb/asm/util/TraceClassVisitor");
mv.visitInsn(DUP);
mv.visitInsn(ACONST_NULL);
mv.visitTypeInsn(NEW, "org/objectweb/asm/util/ASMifier");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/util/ASMifier", "<init>", "()V", false);
mv.visitTypeInsn(NEW, "java/io/PrintWriter");
mv.visitInsn(DUP);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitMethodInsn(INVOKESPECIAL, "java/io/PrintWriter", "<init>", "(Ljava/io/OutputStream;)V", false);
mv.visitMethodInsn(INVOKESPECIAL, "org/objectweb/asm/util/TraceClassVisitor", "<init>", "(Lorg/objectweb/asm/ClassVisitor;Lorg/objectweb/asm/util/Printer;Ljava/io/PrintWriter;)V", false);
mv.visitInsn(ICONST_0);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(13, l1);
mv.visitMethodInsn(INVOKEVIRTUAL, "org/objectweb/asm/ClassReader", "accept", "(Lorg/objectweb/asm/ClassVisitor;I)V", false);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLineNumber(14, l2);
mv.visitInsn(RETURN);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l3, 0);
mv.visitMaxs(8, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
注意
的存在Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(12, l0);
…
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(13, l1);
…
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLineNumber(14, l2);
…
调用 main
方法。
当从命令行 运行 ASMifier
时,它确实默认使用 SKIP_DEBUG
标志,除非 -debug
选项已作为第一个参数给出。