ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using the ASM Toolkit for Bytecode Manipulation
Pages: 1, 2, 3, 4

Testing

First of all, we need to ensure that transformed class is functioning properly after transformation, and that the injected code actually works. These two test cases are represented by the testCounter() and testNotifier() methods below.



public class NotifierClassVisitorTest 
    extends TestCase {
  private TestListener listener;
  private Counter counter;

  public void testCounter() {
    int n1 = counter.count();
    counter.increment();
    int n2 = counter.count();
    assertEquals( n1+1, n2);
  }

  public void testNotifier() {
    counter.count();
    counter.increment();
    counter.count();

    List events = listener.getEvents();
    assertEquals( 3, events.size());
  }
...

The testCounter() method is a typical test case that should ensure that code is functioning as expected. The testNotifier() tests new functionality added by the transformer. In both cases, all initialization is done in the following setUp() method.

  public void setUp() throws Exception {
    super.setUp();
    
    Class cc = loadClass( TEST_CLASS);
    counter = ( Counter) cc.newInstance();
    
    listener = new TestListener();
    (( Notifier) counter).addListener( listener);
  }

The transformed class is loaded in the loadClass() method and a new instance is created. The same instance is cast to the Notifier interface in order to register TestListener, which records notifications and enables retrieving them with the getEvents() method, which is used in testNotifier().

The loadClass() method uses a custom ClassLoader, which transforms classes on the fly using ASM with NotifierClassVisitor.

  private Class loadClass(final String className) throws ClassNotFoundException {
    ClassLoader cl = new TestClassLoader( className);
    return cl.loadClass( className);
  }

The code above assumes that a default constructor exists. The complete source code is available in the Resources section below.

Conclusion

As shown above, ASM allows us to write very compact code for generating new classes and transforming existing bytecode. Using the described approach and ASMifierClassVisitor, it is easy to implement quite advanced transformations. In some cases, it could make sense to use CGLIB which provides code transformation templates and a more high-level API on top of ASM, but a lack of documentation and tutorials make it difficult to learn.

Resources

Eugene Kuleshov is an independent consultant with over 15 years of experience in software design and development.


Return to ONJava.com.