1

I am trying to learn java asm framework for bytecode instrumentation but not able to find sufficient docs or tutorials on it.

I have studied about ClassReader, ClassWriter and ClassVisitor and some more alike APIs but not very clear about how to implement those and how to write corresponding adapters.

Lets say I have a HelloWorld java class.

public class HelloWorld {

    public static void main(String[] args) {
//some code.....

    }

}

Now I want to insert a variable "int i =10;" in the bytecode. Please give me idea about what Adapter/program should I write.

Thanks in advance!

Wojciech Wirzbicki
  • 3,887
  • 6
  • 36
  • 59
Shubham Chaurasia
  • 2,472
  • 2
  • 15
  • 22
  • http://docs.oracle.com/javase/specs/jvms/se8/html/ – Holger Feb 18 '15 at 18:28
  • Have you read: http://download.forge.objectweb.org/asm/asm4-guide.pdf ? There are simple examples there, I believe that explaining what you want to do (I read it some time ago so I'm not sure). – Grzesuav Feb 19 '15 at 07:54

2 Answers2

4

Following is a way to add additional fields to a class such as "int i = 10;". Assuming that you are using javaagent to perform instrumentation: 1) Use the following as the premain class of the java agent

import java.lang.instrument.Instrumentation;

public class SimpleAgent {

    public static void premain(String agentArgs, Instrumentation inst) {

        ClassTransformer transformer = new ClassTransformer();
        inst.addTransformer(transformer);
    }
}   

2) addTransformer calls the transform method of ClassTransformer class which is defined as follows

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class ClassTransformer implements ClassFileTransformer{
    public byte[] transform(ClassLoader    loader,
            String              className,
            Class            classBeingRedefined,
            ProtectionDomain    protectionDomain,
            byte[]              b)
                    throws IllegalClassFormatException {
        try
        {
                ClassReader cr=new ClassReader(b);
                ClassWriter cw = new ClassWriter(cr,ClassWriter.COMPUTE_MAXS);
                AddField cp = new AddField(cw);
                cr.accept(cp,0);
                return cw.toByteArray();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
        return b;
    }
}

3) finally AddField which is as follows is the ClassVisitor that is responsible to add a new field to the class

import static org.objectweb.asm.Opcodes.ASM4;
import org.objectweb.asm.ClassVisitor;


class AddField extends ClassVisitor{

    static String className;
    static String methName, descrip;
    public AddField(ClassVisitor cv) {
        super(ASM4, cv);
    }

    @Override
    public void visit(int version, int access, String name,
            String signature, String superName, String[] interfaces) {
        className = name;
        cv.visit(version, access, name, signature, superName, interfaces);
    }
    public void visitEnd() {
        cv.visitField(0, "i", "I", null , new Integer(10));
        cv.visitEnd();
    }
}

4. ** NEW EDIT ** for adding a variable into a method. The variable has to be stored into a temporary variable and can then be used later. Following adapter can be used for the purpose (look at onMethodEnter):

import static org.objectweb.asm.Opcodes.ASM4;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.AdviceAdapter;

public class MethodAdapter extends ClassVisitor {


    public MethodAdapter(ClassVisitor cv) {
        super(ASM4, cv);
    }

    @Override
    public void visit(int version, int access, String name,
            String signature, String superName, String[] interfaces) {
        cv.visit(version, access, name, signature, superName, interfaces);
    }


    public MethodVisitor visitMethod(int access, String name,
            String desc, String signature, String[] exceptions) {
        MethodVisitor mv;
        mv = cv.visitMethod(access, name, desc, signature, exceptions);
        mv = new AddVariableAdapter(access, name, desc, mv);
        return mv;
    }
    public void visitEnd() {
        cv.visitEnd();
    }


    public class AddVariableAdapter extends AdviceAdapter{
        public AddCallAdapter(int access, String name, String desc,  
                MethodVisitor mv) {  
            super(ASM4, mv, access, name, desc);  
        }  

        protected void onMethodEnter()  {
            mv.visitIntInsn(BIPUSH, 10); // pushes the number 10 on to the stack
            mv.visitVarInsn(ISTORE, 1);  // pops the top of the stack into a local variable indexed by 1
        /*  code to print the local variable
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitVarInsn(ILOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V");*/
        }
    }
}
sri91
  • 182
  • 9
1

A good way to find out how to use ASM is by running the ASMifier tool. If you just want to know how certain language constructors such as variable initializers are converted to bytecode, it might be helpful to create a simple Java class, compile it, locate its .class file and run javap on it or open it with an IDE.

Clashsoft
  • 11,553
  • 5
  • 40
  • 79