Thursday, October 13, 2011

Manipulating Java Class Files with ASM 4 - Part Two: Tree API

What is ASM tree API: ASM Tree API is the part of ASM that lets you create/modify the class in memory. The class is viewed as a tree of information. Like the whole class is an instance of ClassNode, which contain a list of FieldNode objects, a list of MethodNode objects etc. This article assumes that the reader has already read the first part here.

A simple class through tree API: Let's use tree API to create our first class. Again I am going to jump right into a code example, because there is nothing better than a code example. The generated class has a main method that prints "Hello World!".

As you can see, the code is very simple. A primary advantage over BCEL is that unlike BCEL, ASM does not require you to add every constant explicitly to the constant pool. Instead, ASM takes care of the constant pool itself.

Reading a class file: A ClassNode is a ClassVisitor. So, reading a class for use in tree API is as simple as creating a ClassReader object and using it to read a class file, while passing the ClassNode object in its accept method as a parameter. Once this is done, the ClassNode passed is fully initalized with all the information present in the class. In the following example, we will print all the methods in the class.

Modifying a class file: Modifying a class file is a combination of the above two procedures. We first read the class in the usual way, make necessary changes to the data, and then write it back to a file. The following program implements an automatic injection of some logging code. Currently our Logger class only prints to the standard output. Every method annotated with @Loggable will be logged when they begin and when the return. In this we do not log the throw-exception. However that can also be implemented in the same manner by checking opcode ATHROW.

If you run this program, the generated file will have a dependency on the class Logger. Manually copy the Logger class to the correct package in the out directory. If you run the generated class (which is a modified version of LoggingTest class), the following would be the output.

bash-4.1$ java  com.geekyarticles.asm.LoggingTest
Starting method: main
run 1
Starting method: run2
run 2
Ending method: run2
Ending method: main

Note that unlike normal Lists, an InsnList object can be modified while we iterate over it. Any changes are immidiately reflected. So, if some instructions are inserted after the current position, that will also be iterated over.


Post a Comment