ASM建议适配器onMethodEnter - 打印所有参数

时间:2015-03-17 05:26:53

标签: java java-bytecode-asm

我希望使用ASM来打印传递给方法的所有参数的值。我找到了一些例子,但我无法理解它。说实话,我可能还没有完成我的"家庭作业"从某种意义上说,我没有像我需要的那样学习ASM来构建它。但如果有人愿意帮助我,那就太好了。

举个例子,假设该方法返回一个void并将一个整数作为参数。

(我在https://gist.github.com/VijayKrishna/1ca807c952187a7d8c4d看到了例子)

1 个答案:

答案 0 :(得分:0)

好吧我明白了。我们走了。

这是方法注入器类。它读入一个类文件(Print.class)并添加指令以在调用PrintInt方法时打印int

package asm;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;

public class PrintInjector extends ClassVisitor {

    public PrintInjector(int api, ClassVisitor mv) {
        super(api, mv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access,  name,  desc,  signature,  exceptions);
        System.out.println(String.format("%s %s %s", name, desc, signature));
        if(name.equals("PrintInt"))
                mv = new PrintSingleIntParameter(Opcodes.ASM5, mv, access, name, desc);
        return mv;
    }

    public static void main(String[] args) throws IOException {
        ClassReader cr = new ClassReader(new FileInputStream(new File("bin\\Print.class")));
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        PrintInjector pi = new PrintInjector(Opcodes.ASM5, cw);

        cr.accept(pi,  0);

        FileOutputStream fos = new FileOutputStream(new File("Print.class"));
        fos.write(cw.toByteArray());
        fos.flush();
        fos.close();
    }

    class PrintSingleIntParameter extends AdviceAdapter {

        protected PrintSingleIntParameter(int api, MethodVisitor mv, int access,
                String name, String desc) {
            super(api, mv, access, name, desc);
        }

        @Override
        protected void onMethodEnter() {
            Label l0 = new Label();
            mv.visitLabel(l0);
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitVarInsn(ILOAD, 0); //1 instead of 0 if PrintInt wasn't static
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
        }       
    }
}

这是我的Print.class

public class Print {
    public static int PrintInt(int i) {
        System.out.println(i);
        return i;
    }

    public static void main(String[] args) {
        int a = PrintInt(10);
        System.out.println("Done");
    }
}

输出

10
a:10
Done

您需要调整onMethodEnter中的代码以满足您的任何需求。我现在意识到我的代码没有保持字节码干净(因为在我运行此代码后在其他使用寄存器的程序中使用它时会中断它。)