ASMifier недостаточно отображается

#java #java-bytecode-asm

#java #java-байт-код-asm

Вопрос:

Я пытаюсь использовать ASMifier, чтобы точно понять, что находится в моих файлах .class. Проблема в том, что инструмент пропускает определенный mv.visitXXX. Например, он не отображает mv.visitLabel(метка) для меток, объявляющих строки.

Это имеет смысл, потому что ASMifier отображает только mv.visit, необходимый для создания класса, но я хотел бы использовать его по-другому.

Можно ли в любом случае попросить инструмент отображать все, что он посещает?

Ответ №1:

В ASMifier для меток, связанных со строками исходного кода, нет условной обработки, другими словами, если ClassReader сообщает о них, ASMifier они будут показаны.

Чтобы это сработало, должны быть выполнены два условия:

  1. Метод должен иметь LineNumberTable атрибут, связанный с его Code атрибутом (для обычных классов это определяется параметрами компилятора, используемыми при генерации .class файла).

  2. Должно быть, вы не указали SKIP_DEBUG для accept метода ClassReader

При использовании 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 опция не была указана в качестве первого аргумента.

Комментарии:

1. Спасибо, я использовал инструмент ASMifier из командной строки: java -cp «asm-all-5.1.jar » org.objectweb.asm.util. ASMifier Foo.class > Foo.asm но ваше решение идеально подходит для моих нужд.