Analyze Your Classes
Pages: 1, 2, 3
BCEL Basics
BCEL was formerly known as JavaClass. It was incorporated as an Apache Jakarta project in October 2001. The original JavaClass was written by Markus Dahm. The main site is hosted at jakarta.apache.org, from which you can access binaries and source code.
At the heart of BCEL is the JavaClass. A JavaClass represents a Java Class
file as described above, with all of the elements. There is a one-to-one mapping
between the elements of a Java class as described in the JVM specification and
the JavaClass. BCEL thus allows you to read a normal class file in your program
and treat it like any other object. The properties of this object are the Java
class file elements. Furthermore, a JavaClass, which has been created on the
fly within your program, represents an actual class file. If serialized, you
will be able to run this class file in a JVM, as you would do a normally compiled
source file.
BCEL allows you, at a micro level, to model the instructions contained within the Java class file. This way, you can navigate and manipulate this instruction set programmatically, allowing you to introduce enhancements and improvements in the runtime of your class. However, this is not the only way to introduce such enhancements. Better compilers and source code optimization can also do the same trick. Furthermore, it is easier to manipulate source code than it is to manipulate raw bytes. Having said all this, direct byte code manipulation has the advantage of being faster than any enhancement that you can do via compiler or source-code manipulation. This comes at the price of extra complexity. BCEL alleviates this complexity to a certain degree by allowing you to manipulate class files via source code.
Another feature of BCEL is what is called as load-time reflection. As opposed to run-time reflection, which is implemented by using the Reflection API built into the Java language, load-time reflection refers to the ability to modify the byte code instruction set at the time it is loaded. This involves writing a custom classloader, which instead of passing the byte code directly to the JVM, first passes it through your runtime system written using the BCEL API. Your system can then access meta-level objects created at load time and manipulate them. This process can even create these objects without source code present. The result continues normally after this where it is passed to the byte code verifier and then executed in the JVM.
BCEL API
The BCEL API is roughly divided into two parts:
Static API
This is the part of the API that deals with mapping the data structures and binary components described in the JVM specification. You would use this part if you were analyzing existing classes without access to the source code.The main class in this part is called
JavaClass, which represents a Java class and includes all the data structures, constant pool, fields, methods, and commands contained in a class file. It supports the Visitor design pattern, which allows developers to write their own visitor code to traverse and analyze the contents of a class file. TheJavaClassitself derives fromAccessFlagsclass, which is the class that is extended by all classes that have access flags. This thus applies to not onlyJavaClass, but also to theFieldOrMethodclass, the super class forFieldandMethodas well.The Constant Pool is represented by the
ConstantPoolclass. It contains an array of typeConstantthat represents the different constant types in the constant pool of a parsed class file. For example, it may containConstantInteger, which represents reference to anintobject. You can access the constants using an index and by calling the methodgetConstant(int index). Note that this internal array may contain anullreference. This happens in the case ofdoubleorlongreferences that, per the JVM specification, require a skip after an entry.Another interesting class in the API is the
Repositoryclass. This class is used to read existing class files into the system and for resolving class interdependencies.Generic API
This part of the API deals with creating or transforming class files dynamically. It allows you to create a class file from scratch or read an existing class file and dynamically modify it.The central class in this API is the
ClassGenclass. This class allows you to create a new class file and to add methods, attributes and fields to it dynamically. You can load an existing class file in by passing in aJavaClassthat represents a file loaded into memory as described in the Static API. This class also contains methods to search this class for particular methods and fields, to replace existing methods and fields, and remove existing methods and fields. You can also directly use theMethodsGenand theFieldsGenclasses for generating methods and fields, respectively.Corresponding to the
ClassGenclass is theConstantPoolGenclass. This class allows you to add different types of constants and retrieve theConstantPoolonce you are done adding the constants by callinggetFinalConstantPool(). Constants are added using methods likeaddString(String str),addInteger(int n), etc. These methods return the index at which the constant was added. If you are not done adding constants to the pool and yet want to access the ConstantPool in the state that it is in, you can callgetConstantPool(). This class also allows you to look up existing entries in the pool with correspondinglookupString(String str)andlookupInteger(int n)methods.
The BCEL API contains a stack of utility classes that allow you to get started
with the API without worrying too much about the semantics or getting involved
in the complexities. These include Class2HTML, a utility to transform class
files into HTML, JavaWrapper, a utility that acts as a wrapper to modify and
generate classes as they are requested using its own class loader, and BCELifier,
which takes a JavaClass object and generates BCEL Java source code to build
that class.
The following examples start with the utility classes and follow up with simple examples of using the static and dynamic API.
|
Source Code Download the source code for the examples. |
Examples
Important: Make sure that before you run these examples, you have set the CLASSPATH to include bcel.jar.
BCELifier
The easiest way to start with BCEL is to use the BCELifier, because
it generates the BCEL source code used to generate the class file itself and
is a very handy way of learning how BCEL works. It generates the code that
you would have to write yourself if you were to write the BCEL code for generating
the class file dynamically.
The following is a simple HelloWorld source file that I will use for this
example.
public class HelloWorld{
public static void main(String args[]){
System.err.println("Hello World through BCEL!");
}
}
Compile this class and produce the HelloWorld.class file.
Next, run the following command (all on one line) in the directory where you compiled HelloWorld.class:
java org.apache.bcel.util.BCELifier
HelloWorld.class >> HelloWorldCreator.java
Because the BCELifier class outputs the result to standard out, I have
piped the output to the resulting source file. Note that BCELifier creates
the source file as "ClassFileName" + "Creator". Hence, the BCELified
HelloWorld.class gets named HelloWorldCreator.java.
Compile and run HelloWorldCreator.java. You will see the output on the console as: "Hello World through BCEL!".
Open HelloWorldCreator.java and examine it. You will see that creating such a simple class is quite a complex process, even through BCEL abstracts most of the functionality of the Java class file.