美文网首页
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