blob: 26cfc74b1bb7e4735443e1ecd033918878bb9c0d [file] [log] [blame]
\section{Code examples for the BCEL API}\label{sec:apicg}
\subsection{HelloWorldBuilder.java}
The following Java program reads a name from the standard input and
prints a friendly ``Hello''. Since the \texttt{readLine()} method may
throw an \texttt{IOException} it is enclosed by a \texttt{try-catch} block.
{\small \verbatimtabinput{HelloWorld.java}\label{sec:hello}}
\subsection{HelloWorldBuilder.java}
We will sketch here how the above Java class can be created from the
scratch using the \jc API. For ease of reading we will
use textual signatures and not create them dynamically. For example,
the signature
\begin{verbatim}
"(Ljava/lang/String;)Ljava/lang/StringBuffer;"
\end{verbatim}
would actually be created with
\begin{verbatim}
Type.getMethodSignature(Type.STRINGBUFFER, new Type[] { Type.STRING });
\end{verbatim}
\subsubsection{Initialization:}
First we create an empty class and an instruction list:
{\small\begin{verbatim}
ClassGen cg = new ClassGen("HelloWorld", "java.lang.Object",
"<generated>", ACC_PUBLIC | ACC_SUPER,
null);
ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool
InstructionList il = new InstructionList();
\end{verbatim}}
We then create the main method, supplying the method's name and the
symbolic type signature encoded with \texttt{Type} objects.
{\small\begin{verbatim}
MethodGen mg = new MethodGen(ACC_STATIC | ACC_PUBLIC,// access flags
Type.VOID, // return type
new Type[] { // argument types
new ArrayType(Type.STRING, 1) },
new String[] { "argv" }, // arg names
"main", "HelloWorld", // method, class
il, cp);
InstructionFactory factory = new InstructionFactory(cg);
\end{verbatim}}
We define some often use types:
{\small\begin{verbatim}
ObjectType i_stream = new ObjectType("java.io.InputStream");
ObjectType p_stream = new ObjectType("java.io.PrintStream");
\end{verbatim}}
\subsubsection{Create variables \texttt{in} and \texttt{name}:}
We call the constructors, i.e. execute
\texttt{BufferedReader(Input\-Stream\-Reader(System.in))}. The reference
to the \texttt{BufferedReader} object stays on top of the stack and is
stored in the newly allocated \texttt{in} variable.
{\small\begin{verbatim}
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
\end{verbatim}}
Create local variable \texttt{name} and initialize it to \texttt{null}.
{\small\begin{verbatim}
lg = mg.addLocalVariable("name", Type.STRING, null, null);
int name = lg.getIndex();
il.append(InstructionConstants.ACONST_NULL);
lg.setStart(il.append(new ASTORE(name))); // `name' valid from here
\end{verbatim}}
\subsubsection{Create try-catch block}
We remember the start of the block, read a line from the standard
input and store it into the variable \texttt{name}.
{\small\begin{verbatim}
InstructionHandle try_start =
il.append(factory.createFieldAccess("java.lang.System", "out", p_stream,
Constants.GETSTATIC));
il.append(new PUSH(cp, "Please enter your name> "));
il.append(factory.createInvoke("java.io.PrintStream", "print", Type.VOID,
new Type[] { Type.STRING },
Constants.INVOKEVIRTUAL));
il.append(new ALOAD(in));
il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
Type.STRING, Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
il.append(new ASTORE(name));
\end{verbatim}}
Upon normal execution we jump behind exception handler, the target
address is not known yet.
{\small\begin{verbatim}
GOTO g = new GOTO(null);
InstructionHandle try_end = il.append(g);
\end{verbatim}}
We add the exception handler which simply returns from the method.
{\small\begin{verbatim}
InstructionHandle handler = il.append(InstructionConstants.RETURN);
mg.addExceptionHandler(try_start, try_end, handler, "java.io.IOException");
\end{verbatim}}
``Normal'' code continues, now we can set the branch target of the GOTO.
{\small\begin{verbatim}
InstructionHandle ih =
il.append(factory.createFieldAccess("java.lang.System", "out", p_stream,
Constants.GETSTATIC));
g.setTarget(ih);
\end{verbatim}}
\subsubsection{Printing "Hello"}
String concatenation compiles to \texttt{StringBuffer} operations.
{\small\begin{verbatim}
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);
\end{verbatim}}
\subsubsection{Finalization}
Finally, we have to set the stack size, which normally would be
computed on the fly and add a default constructor method to the class,
which is empty in this case.
{\small\begin{verbatim}
mg.setMaxStack(5);
cg.addMethod(mg.getMethod());
il.dispose(); // Allow instruction handles to be reused
cg.addEmptyConstructor(ACC_PUBLIC);
\end{verbatim}}
Last but not least we dump the \texttt{JavaClass} object to a file.
{\small\begin{verbatim}
try {
cg.getJavaClass().dump("HelloWorld.class");
} catch(java.io.IOException e) { System.err.println(e); }
\end{verbatim}}
\subsection{Peephole.java}
This class implements a simple peephole optimizer that removes any NOP
instructions from the given class.
{\small\verbatimtabinput{Peephole.java}}\label{sec:nop}