BCEL - Byte Code Engineering Library (BCEL)
BCEL - Byte Code Engineering Library (BCEL)
BCEL - Byte Code Engineering Library (BCEL)
Abstract
Extensions and improvements of the programming language Java and its related execution environment (Java Virtual
Mac hine, JVM) are the subject of a large number of research projec ts and proposals. There are projects, for instance, to
add parameterized types to Java, to implement Aspect-Oriented Programming, to perform sophisticated static analysis,
and to improve the run-time performance.
Since Java classes are c ompiled into portable binary class files (called byte c ode), it is the most convenient and platform-
independent way to implement these improvements not by writing a new compiler or changing the JVM, but by
transforming the byte c ode. These transformations can either be performed after compile-time, or at load-time. Many
programmers are doing this by implementing their own specialized byte c ode manipulation tools, whic h are, however,
restricted in the range of their re-usability.
To deal with the necessary class file transformations, we introduce an API that helps developers to c onveniently
implement their transformations.
1 Introduction
The Java language has become very popular and many researc h projects deal with further improvements of the language
or its run-time behavior. The possibility to extend a language with new concepts is surely a desirable feature, but the
implementation issues should be hidden from the user. Fortunately, the concepts of the Java Virtual Machine permit the
user-transparent implementation of such extensions with relatively little effort.
Because the target language of Java is an interpreted language with a small and easy-to-understand set of instruc tions
(the byte c ode), developers can implement and test their concepts in a very elegant way. One can write a plug-in
replac ement for the system's class loader which is responsible for dynamically loading c lass files at run-time and passing
the byte c ode to the Virtual Mac hine (see section ). Class loaders may thus be used to interc ept the loading process and
transform c lasses before they get actually executed by the JVM. While the original c lass files always remain unaltered, the
behavior of the c lass loader may be rec onfigured for every exec ution or instrumented dynamically.
The BCEL API (Byte Code Engineering Library), formerly known as JavaClass, is a toolkit for the static analysis and
dynamic creation or transformation of Java class files. It enables developers to implement the desired features on a high
level of abstraction without handling all the internal details of the Java class file format and thus re-inventing the wheel
every time. BCEL is written entirely in Java and freely available under the terms of the Apac he Software License.
This manual is struc tured as follows: We give a brief description of the Java Virtual Machine and the c lass file format in
section 2. Section 3 introduces the BCEL API. Section 4 describes some typic al application areas and example projects.
The appendix c ontains code examples that are to long to be presented in the main part of this paper. All examples are
included in the down-loadable distribution.
Readers already familiar with the Java Virtual Machine and the Java class file format may want to skip this section and
proceed with sec tion 3.
Programs written in the Java language are compiled into a portable binary format c alled byte code. Every c lass is
represented by a single class file containing class related data and byte code instructions. These files are loaded
dynamically into an interpreter (Java Virtual Machine, aka. JVM) and executed.
Figure 1 illustrates the proc edure of c ompiling and executing a Java c lass: The source file (HelloWorld.java) is compiled
into a Java class file (HelloWorld.class), loaded by the byte c ode interpreter and executed. In order to implement
additional features, researc hers may want to transform class files (drawn with bold lines) before they get actually
executed. This application area is one of the main issues of this article.
jakarta.apache.org/bcel/manual.html 1/17
5/18/2009 BCEL - Byte Code Engineering Library …
Giving a full overview of the design issues of the Java c lass file format and the associated byte code instructions is
beyond the sc ope of this paper. We will just give a brief introduc tion covering the details that are nec essary for
understanding the rest of this paper. The format of class files and the byte code instruc tion set are described in more
detail in the Java Virtual Machine Specification. Especially, we will not deal with the security constraints that the Java
Virtual Machine has to check at run-time, i.e. the byte code verifier.
Figure 2 shows a simplified example of the contents of a Java class file: It starts with a header containing a "magic
number" (0xCAFEBABE) and the version number, followed by the c onstant pool, whic h can be roughly thought of as the text
segment of an executable, the acc ess rights of the c lass encoded by a bit mask, a list of interfaces implemented by the
c lass, lists c ontaining the fields and methods of the c lass, and finally the class attributes, e.g., the SourceFile attribute
telling the name of the source file. Attributes are a way of putting additional, user-defined information into class file data
structures. For example, a c ustom class loader may evaluate such attribute data in order to perform its transformations.
The JVM specification declares that unknown, i.e., user-defined attributes must be ignored by any Virtual Machine
implementation.
System.out.println("Hello, world");
The first instruction loads the contents of the field out of c lass java.lang.System onto the operand stack. This is an
instance of the c lass java.io.PrintStream. The ldc ("Load constant") pushes a reference to the string "Hello world" on the
stack. The next instruction invokes the instance method println which takes both values as parameters (Instanc e
methods always implicitly take an instance reference as their first argument).
Instructions, other data structures within the class file and c onstants themselves may refer to c onstants in the constant
pool. Such references are implemented via fixed indexes enc oded directly into the instruc tions. This is illustrated for some
items of the figure emphasized with a surrounding box.
For example, the invokevirtual instruc tion refers to a MethodRef c onstant that contains information about the name of the
c alled method, the signature (i.e., the enc oded argument and return types), and to which c lass the method belongs. In
fac t, as emphasized by the boxed value, the MethodRef constant itself just refers to other entries holding the real data,
jakarta.apache.org/bcel/manual.html 2/17
5/18/2009 BCEL - Byte Code Engineering Library …
e.g., it refers to a ConstantClass entry c ontaining a symbolic referenc e to the class java.io.PrintStream. To keep the
c lass file c ompact, suc h constants are typic ally shared by different instructions and other c onstant pool entries. Similarly,
a field is represented by a Fieldref constant that includes information about the name, the type and the containing c lass
of the field.
The constant pool basically holds the following types of constants: References to methods, fields and classes, strings,
integers, floats, longs, and doubles.
The JVM is a stack-oriented interpreter that c reates a local stac k frame of fixed size for every method invocation. The
size of the local stac k has to be computed by the compiler. Values may also be stored intermediately in a frame area
c ontaining local variables whic h can be used like a set of registers. These local variables are numbered from 0 to 65535,
i.e., you have a maximum of 65536 of local variables per method. The stack frames of caller and callee method are
overlapping, i.e., the c aller pushes arguments onto the operand stac k and the called method rec eives them in loc al
variables.
The byte code instruction set currently consists of 212 instruc tions, 44 opcodes are marked as reserved and may be used
for future extensions or intermediate optimizations within the Virtual Machine. The instruction set can be roughly grouped
as follows:
Stack operations: Constants can be pushed onto the stack either by loading them from the c onstant pool with the ldc
instruc tion or with spec ial "short-cut" instructions where the operand is encoded into the instruc tions, e.g., iconst_0 or
bipush (push byte value).
Arithmetic operations: The instruction set of the Java Virtual Machine distinguishes its operand types using different
instruc tions to operate on values of specific type. Arithmetic operations starting with i, for example, denote an integer
operation. E.g., iadd that adds two integers and pushes the result back on the stack. The Java types boolean, byte,
short, and char are handled as integers by the JVM.
Control flow: There are branch instructions like goto, and if_icmpeq, whic h compares two integers for equality. There is
also a jsr (jump to sub-routine) and ret pair of instructions that is used to implement the finally clause of try-catch
bloc ks. Exceptions may be thrown with the athrow instruction. Branch targets are coded as offsets from the current byte
c ode position, i.e., with an integer number.
Load and store operations for local variables like iload and istore. There are also array operations like iastore whic h
stores an integer value into an array.
Field access: The value of an instance field may be retrieved with getfield and written with putfield. For static fields,
there are getstatic and putstatic c ounterparts.
Method invocation: Static Methods may either be called via invokestatic or be bound virtually with the invokevirtual
instruc tion. Super class methods and private methods are invoked with invokespecial. A special c ase are interfac e
methods which are invoked with invokeinterface.
Object allocation: Class instanc es are allocated with the new instruc tion, arrays of basic type like int[] with newarray,
arrays of references like String[][] with anewarray or multianewarray.
Conversion and type checking: For stack operands of basic type there exist casting operations like f2i whic h converts
a float value into an integer. The validity of a type c ast may be c hec ked with checkcast and the instanceof operator can
be directly mapped to the equally named instruction.
Most instruc tions have a fixed length, but there are also some variable-length instruc tions: In particular, the lookupswitch
and tableswitch instructions, which are used to implement switch() statements. Since the number of case clauses may
vary, these instruc tions contain a variable number of statements.
We will not list all byte c ode instruc tions here, since these are explained in detail in the JVM spec ific ation. The opcode
names are mostly self-explaining, so understanding the following code examples should be fairly intuitive.
Non-abstrac t (and non-native) methods contain an attribute "Code" that holds the following data: The maximum size of
the method's stac k frame, the number of local variables and an array of byte c ode instruc tions. Optionally, it may also
c ontain information about the names of local variables and source file line numbers that can be used by a debugger.
Whenever an exception is raised during execution, the JVM performs exc eption handling by looking into a table of
exception handlers. The table marks handlers, i.e., code chunks, to be responsible for exceptions of certain types that are
raised within a given area of the byte c ode. When there is no appropriate handler the exception is propagated back to the
c aller of the method. The handler information is itself stored in an attribute c ontained within the Code attribute.
Targets of branch instructions like goto are encoded as relative offsets in the array of byte codes. Exception handlers and
local variables refer to absolute addresses within the byte code. The former c ontains references to the start and the end
of the try block, and to the instruction handler c ode. The latter marks the range in which a local variable is valid, i.e., its
sc ope. This makes it difficult to insert or delete code areas on this level of abstraction, sinc e one has to recompute the
jakarta.apache.org/bcel/manual.html 3/17
5/18/2009 BCEL - Byte Code Engineering Library …
offsets every time and update the referring objects. We will see in section 3.3 how BCEL remedies this restric tion.
Java is a type-safe language and the information about the types of fields, local variables, and methods is stored in so
c alled signatures. These are strings stored in the constant pool and enc oded in a special format. For example the
argument and return types of the main method
([java/lang/String;)V
Classes are internally represented by strings like "java/lang/String", basic types like float by an integer number. Within
signatures they are represented by single c harac ters, e.g., I, for integer. Arrays are denoted with a [ at the start of the
signature.
The following example program prompts for a number and prints the factorial of it. The readLine() method reading from the
standard input may raise an IOException and if a misspelled number is passed to parseInt() it throws a
NumberFormatException. Thus, the critical area of code must be encapsulated in a try-catch block.
import java.io.*;
This c ode example typically c ompiles to the following c hunks of byte code:
0: iload_0
1: ifne #8
4: iconst_1
5: goto #16
8: iload_0
9: iload_0
10: iconst_1
11: isub
12: invokestatic Factorial.fac (I)I (12)
15: imul
16: ireturn
jakarta.apache.org/bcel/manual.html 4/17
5/18/2009 BCEL - Byte Code Engineering Library …
fac(): The method fac has only one loc al variable, the argument n, stored at index 0. This variable's scope ranges from
the start of the byte c ode sequence to the very end. If the value of n (the value fetc hed with iload_0) is not equal to 0,
the ifne instruction branches to the byte c ode at offset 8, otherwise a 1 is pushed onto the operand stack and the
c ontrol flow branc hes to the final return. For ease of reading, the offsets of the branc h instructions, which are actually
relative, are displayed as absolute addresses in these examples.
If recursion has to continue, the arguments for the multiplication (n and fac(n - 1)) are evaluated and the results pushed
onto the operand stack. After the multiplication operation has been performed the func tion returns the computed value
from the top of the stack.
0: sipush 4711
3: istore_0
4: getstatic java.lang.System.out Ljava/io/PrintStream;
7: ldc "Please enter a number> "
9: invokevirtual java.io.PrintStream.print (Ljava/lang/String;)V
12: getstatic Factorial.in Ljava/io/BufferedReader;
15: invokevirtual java.io.BufferedReader.readLine ()Ljava/lang/String;
18: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I
21: istore_0
22: goto #44
25: astore_1
26: getstatic java.lang.System.err Ljava/io/PrintStream;
29: aload_1
30: invokevirtual java.io.PrintStream.println (Ljava/lang/Object;)V
33: goto #44
36: astore_1
37: getstatic java.lang.System.err Ljava/io/PrintStream;
40: aload_1
41: invokevirtual java.io.PrintStream.println (Ljava/lang/Object;)V
44: iload_0
45: ireturn
Exception handler(s) =
From To Handler Type
4 22 25 java.io.IOException(6)
4 22 36 NumberFormatException(10)
readInt(): First the loc al variable n (at index 0) is initialized to the value 4711. The next instruction, getstatic, loads the
referencs held by the static System.out field onto the stac k. Then a string is loaded and printed, a number read from the
standard input and assigned to n.
If one of the called methods (readLine() and parseInt()) throws an exception, the Java Virtual Machine c alls one of the
declared exception handlers, depending on the type of the exc eption. The try-clause itself does not produce any code, it
merely defines the range in which the subsequent handlers are ac tive. In the example, the specified source code area
maps to a byte c ode area ranging from offset 4 (inclusive) to 22 (exc lusive). If no exc eption has occurred ("normal"
execution flow) the goto instructions branch behind the handler code. There the value of n is loaded and returned.
The handler for java.io.IOException starts at offset 25. It simply prints the error and branches back to the normal
execution flow, i.e., as if no exc eption had occ urred.
The BCEL API abstracts from the c onc rete circ umstanc es of the Java Virtual Mac hine and how to read and write binary
Java class files. The API mainly c onsists of three parts:
1. A pac kage that contains classes that describe "static" constraints of class files, i.e., reflects the class file format and
is not intended for byte code modifications. The classes may be used to read and write class files from or to a file.
This is useful espec ially for analyzing Java c lasses without having the source files at hand. The main data structure is
called JavaClass which c ontains methods, fields, etc..
2. A pac kage to dynamically generate or modify JavaClass or Method objec ts. It may be used to insert analysis code, to
strip unnec essary information from c lass files, or to implement the code generator bac k-end of a Java compiler.
3. Various code examples and utilities like a class file viewer, a tool to c onvert c lass files into HTML, and a c onverter
from class files to the Jasmin assembly language.
3.1 JavaClass
The "static" component of the BCEL API resides in the package org.apache.bcel.classfile and c losely represents c lass
files. All of the binary components and data structures declared in the JVM specification and described in section 2 are
mapped to c lasses. Figure 3 shows an UML diagram of the hierarchy of classes of the BCEL API. Figure 8 in the appendix
also shows a detailed diagram of the ConstantPool c omponents.
jakarta.apache.org/bcel/manual.html 5/17
5/18/2009 BCEL - Byte Code Engineering Library …
As mentioned in sec tion 2.1 already, several c omponents may contain attribute objects: classes, fields, methods, and
Code objects (introduced in sec tion 2.3). The latter is an attribute itself that contains the ac tual byte code array, the
maximum stack size, the number of loc al variables, a table of handled exceptions, and some optional debugging information
c oded as LineNumberTable and LocalVariableTable attributes. Attributes are in general specific to some data structure,
i.e., no two components share the same kind of attribute, though this is not explic itly forbidden. In the figure the
Attribute c lasses are stereotyped with the c omponent they belong to.
Using the provided Repository class, reading c lass files into a JavaClass object is quite simple:
The repository also contains methods providing the dynamic equivalent of the instanceof operator, and other useful
routines:
if(Repository.instanceOf(clazz, super_class) {
...
}
Information within the class file components may be acc essed like Java Beans via intuitive set/get methods. All of them
jakarta.apache.org/bcel/manual.html 6/17
5/18/2009 BCEL - Byte Code Engineering Library …
also define a toString() method so that implementing a simple class viewer is very easy. In fact all of the examples used
here have been produced this way:
System.out.println(clazz);
printCode(clazz.getMethods());
...
public static void printCode(Method[] methods) {
for(int i=0; i < methods.length; i++) {
System.out.println(methods[i]);
Last but not least, BCEL supports the Visitor design pattern, so one can write visitor objects to traverse and analyze the
c ontents of a class file. Inc luded in the distribution is a class JasminVisitor that converts class files into the Jasmin
assembler language.
3.3 ClassGen
This part of the API (package org.apache.bcel.generic) supplies an abstraction level for creating or transforming c lass files
dynamically. It makes the static c onstraints of Java class files like the hard-coded byte code addresses "generic ". The
generic constant pool, for example, is implemented by the class ConstantPoolGen whic h offers methods for adding different
types of constants. Accordingly, ClassGen offers an interface to add methods, fields, and attributes. Figure 4 gives an
overview of this part of the API.
3.3.1 Types
We abstrac t from the concrete details of the type signature syntax (see 2.5) by introducing the Type class, which is used,
for example, by methods to define their return and argument types. Conc rete sub-classes are BasicType, ObjectType, and
jakarta.apache.org/bcel/manual.html 7/17
5/18/2009 BCEL - Byte Code Engineering Library …
ArrayType which consists of the element type and the number of dimensions. For commonly used types the c lass offers
some predefined constants. For example, the method signature of the main method as shown in section 2.5 is represented
by:
Type also contains methods to c onvert types into textual signatures and vice versa. The sub-classes contain
implementations of the routines and c onstraints specified by the Java Language Spec ific ation.
Fields are represented by FieldGen objec ts, which may be freely modified by the user. If they have the ac cess rights
static final, i.e., are constants and of basic type, they may optionally have an initializing value.
Generic methods contain methods to add exc eptions the method may throw, local variables, and exception handlers. The
latter two are represented by user-c onfigurable objects as well. Because exception handlers and loc al variables c ontain
references to byte code addresses, they also take the role of an instruction targeter in our terminology. Instruc tion
targeters contain a method updateTarget() to redirect a reference. This is somewhat related to the Observer design
pattern. Generic (non-abstract) methods refer to instruction lists that c onsist of instruc tion objects. References to byte
c ode addresses are implemented by handles to instruction objects. If the list is updated the instruc tion targeters will be
informed about it. This is explained in more detail in the following sections.
The maximum stack size needed by the method and the maximum number of local variables used may be set manually or
c omputed via the setMaxStack() and setMaxLocals() methods automatically.
3.3.3 Instructions
Modeling instruc tions as objects may look somewhat odd at first sight, but in fac t enables programmers to obtain a high-
level view upon control flow without handling details like concrete byte code offsets. Instructions c onsist of an opcode
(sometimes called tag), their length in bytes and an offset (or index) within the byte code. Since many instruc tions are
immutable (stack operators, e.g.), the InstructionConstants interfac e offers shareable predefined "fly-weight" constants
to use.
Instructions are grouped via sub-classing, the type hierarchy of instruc tion c lasses is illustrated by (incomplete) figure in
the appendix. The most important family of instructions are the branch instructions, e.g., goto, that branch to targets
somewhere within the byte code. Obviously, this makes them candidates for playing an InstructionTargeter role, too.
Instructions are further grouped by the interfaces they implement, there are, e.g., TypedInstructions that are associated
with a spec ific type like ldc, or ExceptionThrower instructions that may raise exceptions when executed.
All instruc tions can be traversed via accept(Visitor v) methods, i.e., the Visitor design pattern. There is however some
special trick in these methods that allows to merge the handling of c ertain instruction groups. The accept() do not only
c all the corresponding visit() method, but call visit() methods of their respective super classes and implemented
interfac es first, i.e., the most spec ific visit() c all is last. Thus one can group the handling of, say, all BranchInstructions
into one single method.
For debugging purposes it may even make sense to "invent" your own instruc tions. In a sophisticated c ode generator like
the one used as a backend of the Barat framework for static analysis one often has to insert temporary nop (No operation)
instruc tions. When examining the produc ed code it may be very difficult to track back where the nop was actually
inserted. One c ould think of a derived nop2 instruction that contains additional debugging information. When the
instruc tion list is dumped to byte c ode, the extra data is simply dropped.
One c ould also think of new byte c ode instructions operating on complex numbers that are replaced by normal byte code
upon load-time or are recognized by a new JVM.
An instruction list is implemented by a list of instruction handles encapsulating instruction objects. Referenc es to
instruc tions in the list are thus not implemented by direc t pointers to instructions but by pointers to instruction handles.
This makes appending, inserting and deleting areas of c ode very simple and also allows us to reuse immutable instruction
objects (fly-weight objects). Since we use symbolic referenc es, computation of c onc rete byte c ode offsets does not need
to occur until finalization, i.e., until the user has finished the proc ess of generating or transforming code. We will use the
term instruction handle and instruc tion synonymously throughout the rest of the paper. Instruction handles may contain
additional user-defined data using the addAttribute() method.
Appending: One c an append instruc tions or other instruction lists anywhere to an existing list. The instructions are
appended after the given instruction handle. All append methods return a new instruction handle which may then be used
as the target of a branch instruc tion, e.g.:
jakarta.apache.org/bcel/manual.html 8/17
5/18/2009 BCEL - Byte Code Engineering Library …
GOTO g = new GOTO(null);
il.append(g);
...
// Use immutable fly-weight object
InstructionHandle ih = il.append(InstructionConstants.ACONST_NULL);
g.setTarget(ih);
Inserting: Instructions may be inserted anywhere into an existing list. They are inserted before the given instruc tion
handle. All insert methods return a new instruction handle whic h may then be used as the start address of an exception
handler, for example.
Deleting: Deletion of instructions is also very straightforward; all instruction handles and the contained instructions within
a given range are removed from the instruc tion list and disposed. The delete() method may however throw a
TargetLostException when there are instruc tion targeters still referencing one of the deleted instructions. The user is
forc ed to handle such exceptions in a try-catch c lause and redirec t these references elsewhere. The peep hole optimizer
described in the appendix gives a detailed example for this.
try {
il.delete(first, last);
} catch(TargetLostException e) {
InstructionHandle[] targets = e.getTargets();
for(int i=0; i < targets.length; i++) {
InstructionTargeter[] targeters = targets[i].getTargeters();
for(int j=0; j < targeters.length; j++)
targeters[j].updateTarget(targets[i], new_target);
}
}
Finalizing: When the instruction list is ready to be dumped to pure byte code, all symbolic references must be mapped to
real byte code offsets. This is done by the getByteCode() method whic h is called by default by MethodGen.getMethod().
Afterwards you should call dispose() so that the instruction handles can be reused internally. This helps to improve
memory usage.
Using instruction lists gives us a generic view upon the code: In Figure 5 we again present the c ode c hunk of the
readInt() method of the fac torial example in section 2.6: The local variables n and e1 both hold two referenc es to
instruc tions, defining their scope. There are two gotos branching to the iload at the end of the method. One of the
exception handlers is displayed, too: it references the start and the end of the try block and also the exc eption handler
c ode.
jakarta.apache.org/bcel/manual.html 9/17
5/18/2009 BCEL - Byte Code Engineering Library …
To simplify the creation of certain instructions the user can use the supplied InstructionFactory class which offers a lot of
useful methods to c reate instructions from scratch. Alternatively, he can also use compound instructions: When
produc ing byte code, some patterns typically occur very frequently, for instance the compilation of arithmetic or
c omparison expressions. You certainly do not want to rewrite the code that translates such expressions into byte code in
every place they may appear. In order to support this, the BCEL API inc ludes a compound instruction (an interfac e with a
single getInstructionList() method). Instances of this class may be used in any plac e where normal instructions would
occur, particularly in append operations.
Example: Pushing constants Pushing constants onto the operand stack may be coded in different ways. As explained in
section 2.2 there are some "short-c ut" instruc tions that can be used to make the produc ed byte code more c ompac t. The
smallest instruction to push a single 1 onto the stack is iconst_1, other possibilities are bipush (can be used to push
values between -128 and 127), sipush (between -32768 and 32767), or ldc (load c onstant from constant pool).
Instead of repeatedly selec ting the most compact instruction in, say, a switch, one can use the compound PUSH
instruc tion whenever pushing a c onstant number or string. It will produce the appropriate byte c ode instruction and insert
entries into to c onstant pool if nec essary.
When transforming code, for instance during optimization or when inserting analysis method calls, one typically searches
for certain patterns of code to perform the transformation at. To simplify handling such situations BCEL introduces a
special feature: One can search for given c ode patterns within an instruction list using regular expressions. In suc h
expressions, instructions are represented by their opcode names, e.g., LDC, one may also use their respec tive super
c lasses, e.g., "IfInstruction". Meta c harac ters like +, *, and (..|..) have their usual meanings. Thus, the expression
"NOP+(ILOAD|ALOAD)*"
represents a piece of code consisting of at least one NOP followed by a possibly empty sequence of ILOAD and ALOAD
instruc tions.
jakarta.apache.org/bcel/manual.html 10/17
5/18/2009 BCEL - Byte Code Engineering Library …
The search() method of class org.apache.bcel.util.InstructionFinder gets a regular expression and a starting point as
arguments and returns an iterator describing the area of matc hed instruc tions. Additional c onstraints to the matc hing area
of instructions, which can not be implemented via regular expressions, may be expressed via code constraint objects.
In Java, boolean values are mapped to 1 and to 0, respectively. Thus, the simplest way to evaluate boolean expressions is
to push a 1 or a 0 onto the operand stack depending on the truth value of the expression. But this way, the subsequent
c ombination of boolean expressions (with &&, e.g) yields long chunks of code that push lots of 1s and 0s onto the stack.
When the c ode has been finalized these chunks can be optimized with a peep hole algorithm: An IfInstruction (e.g. the
c omparison of two integers: if_icmpeq) that either produces a 1 or a 0 on the stack and is followed by an ifne instruction
(branch if stack value 0) may be replaced by the IfInstruction with its branc h target replaced by the target of the ifne
instruc tion:
The applied c ode c onstraint objec t ensures that the matched code really c orresponds to the targeted expression pattern.
Subsequent application of this algorithm removes all unnecessary stack operations and branc h instructions from the byte
c ode. If any of the deleted instructions is still referenced by an InstructionTargeter object, the referenc e has to be
updated in the catch-c lause.
Example application: The expression:
c an be mapped to both of the c hunks of byte code shown in figure 6. The left column represents the unoptimized code
while the right c olumn displays the same c ode after the peep hole algorithm has been applied:
jakarta.apache.org/bcel/manual.html 11/17
5/18/2009 BCEL - Byte Code Engineering Library …
38: ifeq #52
41: getstatic System.out
44: ldc "Ooops"
46: invokevirtual println
52: return
4 Application areas
There are many possible application areas for BCEL ranging from class browsers, profilers, byte c ode optimizers, and
c ompilers to sophisticated run-time analysis tools and extensions to the Java language.
Compilers like the Barat compiler use BCEL to implement a byte c ode generating back end. Other possible applic ation areas
are the static analysis of byte c ode or examining the run-time behavior of classes by inserting calls to profiling methods
into the code. Further examples are extending Java with Eiffel-like assertions, automated delegation, or with the c onc epts
of Aspec t-Oriented Programming.
A list of projects using BCEL c an be found here.
Class loaders are responsible for loading class files from the file system or other resources and passing the byte c ode to
the Virtual Mac hine. A custom ClassLoader object may be used to intercept the standard procedure of loading a class,
i.e.m the system class loader, and perform some transformations before actually passing the byte code to the JVM.
A possible scenario is desc ribed in figure 7: During run-time the Virtual Machine requests a custom class loader to load a
given class. But before the JVM actually sees the byte code, the class loader makes a "side-step" and performs some
transformation to the class. To make sure that the modified byte code is still valid and does not violate any of the JVM's
rules it is checked by the verifier before the JVM finally executes it.
The former "Poor Man's Generic ity" project that extended Java with parameterized classes, for example, used BCEL in two
plac es to generate instances of parameterized classes: During compile-time (with the standard javac with some slightly
c hanged classes) and at run-time using a c ustom class loader. The compiler puts some additional type information into
c lass files (attributes) which is evaluated at load-time by the class loader. The class loader performs some transformations
on the loaded class and passes them to the VM. The following algorithm illustrates how the load method of the class
loader fulfills the request for a parameterized class, e.g., Stack<String>
1. Searc h for class Stack, load it, and check for a certain class attribute containing additional type information. I.e. the
attribute defines the "real" name of the c lass, i.e., Stack<A>.
2. Replac e all occ urrences and references to the formal type A with referenc es to the ac tual type String. For example
the method
bec omes
jakarta.apache.org/bcel/manual.html 12/17
5/18/2009 BCEL - Byte Code Engineering Library …
3. Return the resulting c lass to the Virtual Machine.
A Appendix
HelloWorldBuilder
The following program reads a name from the standard input and prints a friendly "Hello". Since the readLine() method may
throw an IOException it is enclosed by a try-catch clause.
import java.io.*;
try {
System.out.print("Please enter your name> ");
name = in.readLine();
} catch(IOException e) { return; }
We will sketch here how the above Java class c an be c reated from the sc ratch using the BCEL API. For ease of reading
we will use textual signatures and not create them dynamically. For example, the signature
"(Ljava/lang/String;)Ljava/lang/StringBuffer;"
We then create the main method, supplying the method's name and the symbolic type signature enc oded with Type
objects.
Create variables in and name: We c all the constructors, i.e., execute BufferedReader(InputStreamReader(System.in)). The
reference to the BufferedReader object stays on top of the stack and is stored in the newly allocated in variable.
jakarta.apache.org/bcel/manual.html 13/17
5/18/2009 BCEL - Byte Code Engineering Library …
il.append(factory.createNew("java.io.BufferedReader"));
il.append(InstructionConstants.DUP); // Use predefined constant
il.append(factory.createNew("java.io.InputStreamReader"));
il.append(InstructionConstants.DUP);
il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
Constants.GETSTATIC));
il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
Type.VOID, new Type[] { i_stream },
Constants.INVOKESPECIAL));
il.append(factory.createInvoke("java.io.BufferedReader", "<init>", Type.VOID,
new Type[] {new ObjectType("java.io.Reader")},
Constants.INVOKESPECIAL));
LocalVariableGen lg = mg.addLocalVariable("in",
new ObjectType("java.io.BufferedReader"), null, null);
int in = lg.getIndex();
lg.setStart(il.append(new ASTORE(in))); // "i" valid from here
Create try-catch block: We remember the start of the block, read a line from the standard input and store it into the
variable name.
InstructionHandle try_start =
il.append(factory.createFieldAccess("java.lang.System", "out", p_stream,
Constants.GETSTATIC));
Upon normal execution we jump behind exception handler, the target address is not known yet.
We add the exception handler which simply returns from the method.
"Normal" code continues, now we can set the branc h target of the GOTO.
InstructionHandle ih =
il.append(factory.createFieldAccess("java.lang.System", "out", p_stream,
Constants.GETSTATIC));
g.setTarget(ih);
jakarta.apache.org/bcel/manual.html 14/17
5/18/2009 BCEL - Byte Code Engineering Library …
il.append(factory.createNew(Type.STRINGBUFFER));
il.append(InstructionConstants.DUP);
il.append(new PUSH(cp, "Hello, "));
il.append(factory.createInvoke("java.lang.StringBuffer", "<init>",
Type.VOID, new Type[] { Type.STRING },
Constants.INVOKESPECIAL));
il.append(new ALOAD(name));
il.append(factory.createInvoke("java.lang.StringBuffer", "append",
Type.STRINGBUFFER, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL));
il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
Type.STRING, Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
il.append(factory.createInvoke("java.io.PrintStream", "println",
Type.VOID, new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL));
il.append(InstructionConstants.RETURN);
Finalization: Finally, we have to set the stac k size, which normally would have to be computed on the fly and add a
default constructor method to the class, which is empty in this case.
mg.setMaxStack();
cg.addMethod(mg.getMethod());
il.dispose(); // Allow instruction handles to be reused
cg.addEmptyConstructor(ACC_PUBLIC);
try {
cg.getJavaClass().dump("HelloWorld.class");
} catch(java.io.IOException e) { System.err.println(e); }
Peephole optimizer
This c lass implements a simple peephole optimizer that removes any NOP instructions from the given class.
import java.io.*;
import java.util.Iterator;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.*;
import org.apache.bcel.Repository;
import org.apache.bcel.util.InstructionFinder;
jakarta.apache.org/bcel/manual.html 15/17
5/18/2009 BCEL - Byte Code Engineering Library …
*/
clazz.setConstantPool(cp.getFinalConstantPool());
clazz.dump(clazz.getClassName() + "_.class");
} catch(Exception e) { e.printStackTrace(); }
}
count += match.length;
Method m = null;
if(count > 0) {
System.out.println("Removed " + count + " NOP instructions from method " +
mg.getName());
m = mg.getMethod();
}
BCELifier
If you want to learn how certain things are generated using BCEL you can do the following: Write your program with the
needed features in Java and c ompile it as usual. Then use BCELifier to create a class that c reates that very input class
using BCEL.
(Think about this sentence for a while, or just try it ...)
jakarta.apache.org/bcel/manual.html 16/17
5/18/2009 BCEL - Byte Code Engineering Library …
jakarta.apache.org/bcel/manual.html 17/17