美文网首页
05 - 如何编写ASM代码

05 - 如何编写ASM代码

作者: 舍是境界 | 来源:发表于2022-01-13 07:12 被阅读0次

在刚开始学习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代码,作为思考的起点。

相关文章

网友评论

      本文标题:05 - 如何编写ASM代码

      本文链接:https://www.haomeiwen.com/subject/qclycrtx.html