Using the ASM Toolkit for Bytecode Manipulation

Using the ASM Toolkit for Bytecode Manipulation

Let's take them one by one, but I should remind you that ASM's visitors can be chained very much the same way as SAX's handlers or filters. This sequence UML diagram shows class transformation, where green classes will be substituted by custom NotifierClassVisitor and NotifierCodeVisitor that will do the actual bytecode transformation.



The code below uses NotifierClassVisitor to apply all required transformations.

byte[] bytecode;

...

ClassWriter cw = new ClassWriter(true);



NotifierClassVisitor ncv = 

    new NotifierClassVisitor(cw)



ClassReader cr = new ClassReader(bytecode);

cr.accept(ncv);

Notice the true parameter in the ClassWriter constructor, which enables the automatic calculation of maximum size of stack and local variables. In this case, all values passed to the CodeVisitor.visitMax() method will be ignored and ClassWriter will calculate these values based on the actual bytecode of the method. However, the CodeVisitor.visitMax() method still must be called, which happens in its default implementation in CodeAdapter. This is important because, as you can see in the comparison results, these values are different for changed bytecode, and with this flag they will be recalculated automatically, covering item #6 in the list above. The rest of items will be handled by NotifierClassVisitor.

public class NotifierClassVisitor 

    extends ClassAdapter implements Constants {

  ...

The first difference appears in parameters of the visit method, where the new interface should be added. The code below will cover item #1. Notice that the cv.visit() method is called to redirect the transformed processing event to the nested class visitor, which is actually going to be a ClassWriter object. We also need to save the class name, since it will be needed later.

public void visit( int version, int access, 

    String name, String superName,

    String[] interfaces, String sourceFile) {

  this.className = name;



  String[] c;

  if( interfaces==null) {

    c = new String[ 1];

  } else {

    int n = 1+interfaces.length;

    c = new String[ n];

    System.arraycopy(interfaces, 0, c, 0, n);

  }

  c[ c.length-1] = Notifier.class.getName(); 

  cv.visit( version, access, name, superName,

      c, sourceFile);

}

All new elements can be added in the visitEnd() method just before calling visitEnd() on the chained visitor. That will cover items #2 and #3 from the list above. Notice that the class name saved in the visit() method is used instead of a hard-coded constant, which makes the transformation more generic.

public void visitEnd() {

  // adding new field

  cv.visitField(ACC_PRIVATE, "__lst", 

      "Ljava/util/ArrayList;", null, null);



  // adding new methods

  CodeVisitor cd;

  {

  cd = cv.visitMethod(ACC_PUBLIC, "notify", 

      "(Ljava/lang/String;)V", null, null);

  cd.visitInsn(ICONST_0);

  cd.visitVarInsn(ISTORE, 2);

  Label l0 = new Label();

  cd.visitLabel(l0);

  cd.visitVarInsn(ILOAD, 2);

  cd.visitVarInsn(ALOAD, 0);

  cd.visitFieldInsn(GETFIELD, className,

      "__lst", "Ljava/util/ArrayList;");

  ... 

  ... see diff above

  ... 

  cd.visitInsn(RETURN);

  cd.visitMaxs(1, 1);

  }

  {

  cd = cv.visitMethod(ACC_PUBLIC, "addListener",

     "(Lasm1/Listener;)V", null, null);

  cd.visitVarInsn(ALOAD, 0);

  ... 

  ... see diff above

  ... 

  cd.visitInsn(RETURN);

  cd.visitMaxs(1, 1);

  }



  cv.visitEnd();

}

The rest of the changes belong to method bytecode, so it's necessary to overwrite the visitMethod() method. There are two cases have to be covered:

  • Add instructions to call notify() method to all non-static methods.
  • Add initialization code to all <init> methods.

In the first case, new instructions are always added to the beginning of the method bytecode, so chained CodeVisitor can be fired directly. However, in case of the <init> method, instructions should be added to the end of method, so they have to be inserted before visitInsn(RETURN), meaning a custom CodeVisitor is required here. This is how visitMethod() will look:

public CodeVisitor visitMethod( int access,

    String name, String desc, 

    String[] exceptions, Attribute attrs) {

  CodeVisitor cd = cv.visitMethod( access, 

      name, desc, exceptions, attrs);

  if( cd==null) return null;



  if( "<init>".equals( name)) {

    return new NotifierCodeVisitor( cd, className);

  }

  if((access & Constants.ACC_STATIC)==0) {

    // insert instructions to call notify()

    cd.visitVarInsn(ALOAD, 0);

    cd.visitLdcInsn(name+desc);

    cd.visitMethodInsn(INVOKEVIRTUAL, className,

        "notify", "(Ljava/lang/String;)V");

  }

  return cd;

}

Similar to ClassAdapter, we can extend the CodeAdapter class and overwrite only those methods that should change the stream of processing events. In this case, we change the visitInsn() method to verify if it is an event for the RETURN command and, if so, insert required commands before delegating the event to the next CodeVisitor in the chain.

public class NotifierCodeVisitor 

    extends CodeAdapter {

  ...



  public void visitInsn( int opcode) {

    if( opcode==RETURN) {

      String type = "java/util/ArrayList";

      cv.visitVarInsn(ALOAD,0);

      cv.visitTypeInsn(NEW,type);

      cv.visitInsn(DUP);

      cv.visitMethodInsn(INVOKESPECIAL, 

          type,"<init>","()V");

      cv.visitFieldInsn(PUTFIELD, "asm1/Counter",

          "__lst", "L"+type+";");

    }

    cv.visitInsn(opcode);

  }

}

That is basically it. The only piece we have left is the unit test for the whole transformation.

Prev  [1] [2] [3] [4] Next

Close    To Top
  • Prev Article-Java:
  • Next Article-Java:
  • Now: Tutorial for Web and Software Design > Java > Java Basic > Java Content
    Photoshop Tutorial
     

    Special Effect

      3D Effect
      Photoshop Articles
    Programming Tutorial
     

    C/C++ Tutorial

      Visual Basic
      C# Tutorial
    Database Tutorial
     

    MySQL Tutorial

      MS SQL Tutorial
      Oracle Tutorial
    Geek Tutorial
     

    Blogging Tutorial

      RSS Tutorial
      Podcasting Tutorial
    Graphic Design Tutorial
      Coreldraw Tutorial
      Illustrator Tutorial
      3D Tutorials
    Webmaster Articles
     

    Domain Service

      Web Hosting
      Site Promotion
    Java Tutorial/ Articles
     

    Java Servlets

      JavaEE Tutorial
     

    JavaBeans Tutorial

    XML Tutorial/ Articles
     

    XML Style

      AJAX Tutorial
      XML Mobile
    Flash Tutorial/ Articles
     

    Flash Video

      Action Script
      Flash Articles
    OS Tutorial/ Articles
      Linux Tutorial
      Symbian Tutorial
      MacOS Tutorial
    Personal Tech
      Hardware Tutorial
      Software Tutorial
      Online Auction