Search code examples
javajvminstrumentationjava-bytecode-asmjavaagents

"VerifyError: Accessing value from uninitialized register" when using ASM and javaagent to collect method arguments


When entering a method, I want to record all the arguments of that method. Since ASM can be hard to debug, I write some demo code(use @apangin's collector class) and test its validity. However, it reports java.lang.VerifyError: (class: com/D, method: add signature: (II)I) Accessing value from uninitialized register 2 when I use the following code.

I think when a frame is created, the local variables are initialized by this and the arguments of the method. But register 2 (I guess it means the third element in local variables) should be initialized by the second int argument, why it still throws the error?

The code of com/D:

public class D extends C {
    public D() { }

    public int a = 4;

    @Override
    public int p() { return a; }

    public static int add(int a, int b){
        return a+b;
    }
}

The visitCode method in my methodVisitor adapter class:

    public void visitCode() {
        mv.visitCode();
        if (needAnalysePurity){
            pushArgArrToStackInMeth();
            mv.visitInsn(POP);
        }
    }
    public void pushArgArrToStackInMeth(){
        Type[] args = Type.getArgumentTypes(this.selfDesc);
        // new ArgumentCollector(N)
        String collector = Type.getInternalName(ArgCollectorLocals.class);
        mv.visitTypeInsn(NEW, collector);
        mv.visitInsn(DUP);
        mv.visitIntInsn(SIPUSH, args.length);
        mv.visitMethodInsn(INVOKESPECIAL, collector, "<init>", "(I)V", false);

        // for each argument call the corresponding collector.add(arg)
        int index = 1;
        for (Type arg : args) {
            String argDesc = arg.getDescriptor();

            if (argDesc.length() > 1) {
                argDesc = "Ljava/lang/Object;";
            }
            mv.visitVarInsn(arg.getOpcode(ILOAD), index);
            mv.visitMethodInsn(INVOKEVIRTUAL, collector, "add", "(" + argDesc + ")L" + collector + ";", false);
            index += arg.getSize();
        }
        // collector.toArray()
        mv.visitMethodInsn(INVOKEVIRTUAL, collector, "toArray", "()[Ljava/lang/Object;", false);
    }

The collector class used to collect the arguments:

public class ArgCollectorLocals {
    private final Object[] args;
    private int index;

    public ArgCollectorLocals(int length) {
        this.args = new Object[length];
    }

    public ArgCollectorLocals add(boolean a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(byte a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(char a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(short a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(int a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(long a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(float a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(double a) {
        args[index++] = a;
        return this;
    }

    public ArgCollectorLocals add(Object a) {
        args[index++] = a;
        return this;
    }

    public Object[] toArray() {
        return args;
    }
}

Solution

  • Thanks to @apangin, I finally solved this problem. Besides the method arguments, I also want to record the instance that the method belongs to (if the method is not static), so I have the following code:

        public void pushArgArrToStackInMeth(){
            Type[] args = Type.getArgumentTypes(this.selfDesc);
            // new ArgumentCollector(N)
            String collector = Type.getInternalName(ArgCollectorLocals.class);
            mv.visitTypeInsn(NEW, collector);
            mv.visitInsn(DUP);
            if (isStatic){
                mv.visitIntInsn(SIPUSH, args.length);
            }else{
                mv.visitIntInsn(SIPUSH, args.length + 1);  // for this
            }
            mv.visitMethodInsn(INVOKESPECIAL, collector, "<init>", "(I)V", false);
    
            // record "this" if not static
            if (!isStatic){
                mv.visitVarInsn(ALOAD, 0);
                mv.visitMethodInsn(INVOKEVIRTUAL, collector, "add", "(Ljava/lang/Object;)L" + collector + ";", false);
            }
    
            // for each argument call the corresponding collector.add(arg)
            int index = 1;
            if (isStatic){
                index = 0;
            }
            for (Type arg : args) {
                String argDesc = arg.getDescriptor();
                if (argDesc.length() > 1) {
                    argDesc = "Ljava/lang/Object;";
                }
                mv.visitVarInsn(arg.getOpcode(ILOAD), index);
                mv.visitMethodInsn(INVOKEVIRTUAL, collector, "add", "(" + argDesc + ")L" + collector + ";", false);
                index += arg.getSize();
            }
            // collector.toArray()
            mv.visitMethodInsn(INVOKEVIRTUAL, collector, "toArray", "()[Ljava/lang/Object;", false);
        }