| \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} |