在刚开始学习ASM的时候,编写ASM代码是不太容易的。或者,有些人原来对ASM很熟悉,但由于长时间不使用ASM,编写ASM代码也会有一些困难。在本文当中,我们介绍一个ASMPrint类,它能帮助我们将.class文件转换为ASM代码,这个功能非常实用。
ASMPrint类
下面是ASMPrint类的代码,它是利用org.objectweb.asm.util.TraceClassVisitor类来实现的。在使用的时候,我们注意修改一下className、parsingOptions和asmCode参数就可以了。
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.util.ASMifier;
import jdk.internal.org.objectweb.asm.util.Printer;
import jdk.internal.org.objectweb.asm.util.Textifier;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import java.io.IOException;
import java.io.PrintWriter;
public class ASMPrint {
public static void main(String[] args) throws IOException {
// (1) 设置参数
String className = "sample.HelloWorld";
int parsingOptions = ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG;
boolean asmCode = true;
// (2) 打印结果
Printer printer = asmCode ? new ASMifier() : new Textifier();
PrintWriter printWriter = new PrintWriter(System.out, true);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, printWriter);
new ClassReader(className).accept(traceClassVisitor, parsingOptions);
}
}
在现在阶段,我们主要是使用这个类,来帮助我们生成ASM代码;
ASMPrint类使用示例
假如,有如下一个HelloWorld类:
public class HelloWorld {
public void test() {
System.out.println("Test Method");
}
}
对于ASMPrint类来说,其中
- className值设置为类的全限定名,可以是我们自己写的类,例如sample.HelloWorld,也可以是JDK自带的类,例如java.lang.Comparable。
- asmCode值设置为true或false。如果是true,可以打印出对应的ASM代码;如果是false,可以打印出方法对应的Instruction。
- parsingOptions值设置为ClassReader.SKIP_CODE、ClassReader.SKIP_DEBUG、ClassReader.SKIP_FRAMES、ClassReader.EXPAND_FRAMES的组合值,也可以设置为0,可以打印出详细程度不同的信息。
运行结果示例:
package asm.sample;
import java.util.*;
import jdk.internal.org.objectweb.asm.*;
public class HelloWorldDump 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, "sample/HelloWorld", null, "java/lang/Object", new String[] { "java/lang/Cloneable" });
{
fv = cw.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "intValue", "I", null, new Integer(10));
fv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
mv.visitCode();
mv.visitInsn(ICONST_1);
mv.visitVarInsn(ISTORE, 1);
mv.visitInsn(ICONST_2);
mv.visitVarInsn(ISTORE, 2);
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitInsn(IADD);
mv.visitVarInsn(ISTORE, 3);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 4);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
mv.visitParameter("args", 0);
mv.visitCode();
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("HelloWorld");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
小结
本文主要介绍了ASMPrint类和它的使用示例,内容总结如下:
- ASMPrint类,是通过org.objectweb.asm.util.TraceClassVisitor实现的。
- ASMPrint类的作用,是帮助我们生成ASM代码。当我们想实现某一个功能时,不知道如何下手,可以使用ASMPrint类生成的ASM代码,作为思考的起点。
网友评论