Using the ASM Toolkit for Bytecode Manipulation

Using the ASM Toolkit for Bytecode Manipulation

ASM Overview

Before looking at bytecode transformation, we need a better understanding of the events defined for the ClassVisitor interface.



These events should come in the following order and contain parameters as described below.

Once visit Class access flags (public, private, static, etc.), bytecode version, name, super class, implemented interfaces, and source file name.
Multiple
times
visitField Field access flags, name and signature, init value, and field attributes (e.g., annotations).
visitMethod Method access flags, name and signature and method attributes.
visitInnerClass Inner class access flags, its name and outer name
visitAttribute Class-level attributes
Once visitEnd Complete processing

visitMethod is different from the others, because it returns a new instance of CodeVisitor for every call. That instance will handle processing events for method bytecode (including method and parameter attributes, information for try-catch blocks, etc.).

The table below outlines the methods of CodeVisitor. These methods must be called in the sequential order of the bytecode instructions of the visited code. Each method can either handle bytecode instructions grouped by the similar parameters or other bytecode artifacts, such as the local variable table, line numbers, try-catch blocks, and nonstandard attributes (marked grey in the table below).

visitInsn Visits a zero operand instruction: NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT.
visitFieldInsn Visits a field instructions: GETSTATIC, PUTSTATIC, GETFIELD, or PUTFIELD.
visitIntInsn Visits an instruction with a single int operand: BIPUSH, SIPUSH, or NEWARRAY.
visitJumpInsn Visits a jump instruction: IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL, or IFNONNULL.
visitTypeInsn Visits a type instruction: NEW, ANEWARRAY, CHECKCAST, or INSTANCEOF.
visitVarInsn Visits a local variable instruction: ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, or RET.
visitMethodInsn Visits a method instruction: INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, or INVOKEINTERFACE.
visitIincInsn Visits an IINC instruction.
visitLdcInsn Visits a LDC instruction.
visitMultiANewArrayInsn Visits a MULTIANEWARRAY instruction.
visitLookupSwitchInsn Visits a LOOKUPSWITCH instruction.
visitTableSwitchInsn Visits a TABLESWITCH instruction.
visitLabel Visits a label.
visitLocalVariable Visits a local variable declaration.
visitLineNumber Visits a line-number declaration.
visitTryCatchBlock Visits a try-catch block.
visitMaxs Visits the maximum stack size and the maximum number of local variables of the method.
visitAttribute Visits a non-standard attribute of the code.

The visitMaxs method is called after all of the instructions have been visited. The visitTryCatchBlock, visitLocalVariable, and visitLineNumber methods may be called in any order, at any time (provided the labels passed as arguments have already been visited with visitLabel).

In order to specify positions in the method bytecode and not have to use absolute offsets, ASM uses the Label class. Label instances are passed as parameters of visitJumpInsn, visitLookupSwitchInsn, visitTableSwitchInsn, visitTryCatchBlock, visitLocalVariable, and visitLineNumber, to refer to a specific place in method code; a visitLabel method with the same Label instance is used to actually mark that place.

The next section shows how the ClassVisitor and CodeVisitor interfaces can work together in a bytecode transformation scenario.

Bytecode Transformation

Imagine that we need to transform some classes in the runtime, and implement the Notifier interface from the example above. In our case, all registered observers should receive events when any of the methods of the original class have been called. We can pick some simple class and use ASMifierClassVisitor to see what the transformation should look like.

For example:

public class Counter1 {

  private int n;

    

  public void increment() {

    n++;

  }



  private int count() {

    return n;

  }



}

After implementing the Notifier interface, this class may look like something like the following:

import java.util.ArrayList;

import java.util.Observer;



public class Counter2 implements Notifier {

  private int n;

  private ArrayList __lst = new ArrayList();

    

  public void increment() {

    notify( "increment()");

    n++;

  }



  private int count() {

    notify( "count()");

    return n;

  }





  // Listener implementation



  public void notify( String msg) {

    for( int i = 0; i<__lst.size(); i++) {

      ((Listener)__lst.get(i)).update(this, msg);

    }

  }



  public void addListener( Listener listener) {

    __lst.add( listener);

  }



}

Now you can compile both sources, run ASMifierClassVisitor as described above, and then compare the resulting files using your favorite diff application. Here are the comparison results. Removed lines are shown in red with a minus sign (-) at the left, while additions are shown in green with a plus sign (+).

  ...

  ClassWriter cw = new ClassWriter(false);

  CodeVisitor cv;

- cw.visit(ACC_PUBLIC + ACC_SUPER, "asm1/Counter1",
+ cw.visit(ACC_PUBLIC + ACC_SUPER, "asm1/Counter2",
      "java/lang/Object",
-     null,
+     new String[] { "asm1/Notifier" },
[ 1 ]
-     "Counter1.java");
+     "Counter2.java");


  cw.visitField(ACC_PRIVATE, "n", "I",null,null);



+ cw.visitField(ACC_PRIVATE, "__lst", 

+     "Ljava/util/ArrayList;", null, null);
[ 2 ]


  {

  cv = cw.visitMethod(ACC_PUBLIC, 

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

  cv.visitVarInsn(ALOAD, 0);

  cv.visitMethodInsn(INVOKESPECIAL, 

      "java/lang/Object", "<init>", "()V");
+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitTypeInsn(NEW, "java/util/ArrayList");

+ cv.visitInsn(DUP);

+ cv.visitMethodInsn(INVOKESPECIAL, 

+     "java/util/ArrayList", "<init>", "()V");

+ cv.visitFieldInsn(PUTFIELD, "asm1/Counter2", 

+     "__lst", "Ljava/util/ArrayList;");
[ 3 ]
  cv.visitInsn(RETURN);
- cv.visitMaxs(1, 1);
+ cv.visitMaxs(3, 1);
[ 4 ]
  }

  {

  cv = cw.visitMethod(ACC_PUBLIC, "increment", 

      "()V", null, null);
+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitLdcInsn("increment()");

+ cv.visitMethodInsn(INVOKEVIRTUAL, "asm1/Counter2", 

+     "notify", "(Ljava/lang/String;)V");
[ 5 ]
  cv.visitVarInsn(ALOAD, 0);

  cv.visitInsn(DUP);
- cv.visitFieldInsn(GETFIELD, "asm1/Counter1","n","I");
+ cv.visitFieldInsn(GETFIELD, "asm1/Counter2","n","I");
  cv.visitInsn(ICONST_1);

  cv.visitInsn(IADD);
- cv.visitFieldInsn(PUTFIELD, "asm1/Counter1","n","I");
+ cv.visitFieldInsn(PUTFIELD, "asm1/Counter2","n","I");
  cv.visitInsn(RETURN);

  cv.visitMaxs(3, 1);

  }

  {

  cv = cw.visitMethod(ACC_PRIVATE, "count", 

      "()I", null, null);
+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitLdcInsn("count()");

+ cv.visitMethodInsn(INVOKEVIRTUAL, "asm1/Counter2", 

+     "notify", "(Ljava/lang/String;)V");
[ 5 ]
  cv.visitVarInsn(ALOAD, 0);
- cv.visitFieldInsn(GETFIELD, "asm1/Counter1","n","I");
+ cv.visitFieldInsn(GETFIELD, "asm1/Counter2","n","I");
  cv.visitInsn(IRETURN);
- cv.visitMaxs(1, 1);
+ cv.visitMaxs(2, 1);
[ 4 ]
  }

  {
+ cv = cw.visitMethod(ACC_PUBLIC, "notify", 

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

+ cv.visitInsn(ICONST_0);

+ cv.visitVarInsn(ISTORE, 2);

+ Label l0 = new Label();

+ cv.visitLabel(l0);

+ cv.visitVarInsn(ILOAD, 2);

+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitFieldInsn(GETFIELD, "asm1/Counter2", 

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

+ cv.visitMethodInsn(INVOKEVIRTUAL, 

+     "java/util/ArrayList", "size", "()I");

+ Label l1 = new Label();

+ cv.visitJumpInsn(IF_ICMPGE, l1);

+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitFieldInsn(GETFIELD, "asm1/Counter2", 

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

+ cv.visitVarInsn(ILOAD, 2);

+ cv.visitMethodInsn(INVOKEVIRTUAL, 

+     "java/util/ArrayList", "get", 

+     "(I)Ljava/lang/Object;");

+ cv.visitTypeInsn(CHECKCAST, "asm1/Listener");

+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitVarInsn(ALOAD, 1);

+ cv.visitMethodInsn(INVOKEINTERFACE,

+     "asm1/Listener", "notify", 

+     "(Ljava/lang/Object;Ljava/lang/Object;)V");

+ cv.visitIincInsn(2, 1);

+ cv.visitJumpInsn(GOTO, l0);

+ cv.visitLabel(l1);

+ cv.visitInsn(RETURN);

+ cv.visitMaxs(3, 3);
[ 6 ]
  }

  {
+ cv = cw.visitMethod(ACC_PUBLIC, "addListener",

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

+ cv.visitVarInsn(ALOAD, 0);

+ cv.visitFieldInsn(GETFIELD, "asm1/Counter2",

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

+ cv.visitVarInsn(ALOAD, 1);

+ cv.visitMethodInsn(INVOKEVIRTUAL, 

+    "java/util/ArrayList", "add", 

+    "(Ljava/lang/Object;)Z");

+ cv.visitInsn(POP);

+ cv.visitInsn(RETURN);

+ cv.visitMaxs(2, 2);
[ 6 ]
  }

  cw.visitEnd();

  ...

You can see the following groups of changes:

  1. A new interface was added to the class declaration.
  2. One new field was added.
  3. Some instructions were added to the end of the <init> method, representing code for constructor and class initialization.
  4. visitMaxs() have different parameters (used stack has been changed in modified bytecode).
  5. Some instructions were added to the beginning of existing class methods.
  6. Two new methods were added.

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