Home | History | Annotate | Download | only in manual
      1 <?xml version="1.0"?>
      2 <!--
      3     * Licensed to the Apache Software Foundation (ASF) under one
      4     * or more contributor license agreements.  See the NOTICE file
      5     * distributed with this work for additional information
      6     * regarding copyright ownership.  The ASF licenses this file
      7     * to you under the Apache License, Version 2.0 (the
      8     * "License"); you may not use this file except in compliance
      9     * with the License.  You may obtain a copy of the License at
     10     * 
     11     *   http://www.apache.org/licenses/LICENSE-2.0
     12     * 
     13     * Unless required by applicable law or agreed to in writing,
     14     * software distributed under the License is distributed on an
     15     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     16     * KIND, either express or implied.  See the License for the
     17     * specific language governing permissions and limitations
     18     * under the License.    
     19 -->
     20 <document>
     21   <properties>
     22     <title>Appendix</title>
     23   </properties>
     24 
     25   <body>
     26     <section name="Appendix">
     27 
     28     <subsection name="HelloWorldBuilder">
     29       <p>
     30         The following program reads a name from the standard input and
     31         prints a friendly "Hello". Since the <tt>readLine()</tt> method may
     32         throw an <tt>IOException</tt> it is enclosed by a <tt>try-catch</tt>
     33         clause.
     34       </p>
     35 
     36       <source>
     37 import java.io.*;
     38 
     39 public class HelloWorld {
     40     public static void main(String[] argv) {
     41         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
     42         String name = null;
     43 
     44         try {
     45             System.out.print("Please enter your name&gt; ");
     46             name = in.readLine();
     47         } catch (IOException e) {
     48             return;
     49         }
     50 
     51         System.out.println("Hello, " + name);
     52     }
     53 }
     54       </source>
     55 
     56       <p>
     57         We will sketch here how the above Java class can be created from the
     58         scratch using the <font face="helvetica,arial">BCEL</font> API. For
     59         ease of reading we will use textual signatures and not create them
     60         dynamically. For example, the signature
     61       </p>
     62 
     63       <source>"(Ljava/lang/String;)Ljava/lang/StringBuffer;"</source>
     64 
     65       <p>
     66         actually be created with
     67       </p>
     68 
     69       <source>Type.getMethodSignature(Type.STRINGBUFFER, new Type[] { Type.STRING });</source>
     70 
     71       <p><b>Initialization:</b>
     72         First we create an empty class and an instruction list:
     73       </p>
     74 
     75       <source>
     76 ClassGen cg = new ClassGen("HelloWorld", "java.lang.Object",
     77                             "&lt;generated&gt;", ACC_PUBLIC | ACC_SUPER, null);
     78 ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool
     79 InstructionList il = new InstructionList();
     80       </source>
     81 
     82       <p>
     83         We then create the main method, supplying the method's name and the
     84         symbolic type signature encoded with <tt>Type</tt> objects.
     85       </p>
     86 
     87       <source>
     88 MethodGen  mg = new MethodGen(ACC_STATIC | ACC_PUBLIC, // access flags
     89                               Type.VOID,               // return type
     90                               new Type[] {             // argument types
     91                               new ArrayType(Type.STRING, 1) },
     92                               new String[] { "argv" }, // arg names
     93                               "main", "HelloWorld",    // method, class
     94                               il, cp);
     95 InstructionFactory factory = new InstructionFactory(cg);
     96       </source>
     97 
     98       <p>
     99         We now define some often used types:
    100       </p>
    101 
    102       <source>
    103 ObjectType i_stream = new ObjectType("java.io.InputStream");
    104 ObjectType p_stream = new ObjectType("java.io.PrintStream");
    105       </source>
    106 
    107       <p><b>Create variables <tt>in</tt> and <tt>name</tt>:</b> We call
    108         the constructors, i.e., execute
    109         <tt>BufferedReader(InputStreamReader(System.in))</tt>. The reference
    110         to the <tt>BufferedReader</tt> object stays on top of the stack and
    111         is stored in the newly allocated <tt>in</tt> variable.
    112       </p>
    113 
    114       <source>
    115 il.append(factory.createNew("java.io.BufferedReader"));
    116 il.append(InstructionConstants.DUP); // Use predefined constant
    117 il.append(factory.createNew("java.io.InputStreamReader"));
    118 il.append(InstructionConstants.DUP);
    119 il.append(factory.createFieldAccess("java.lang.System", "in", i_stream, Constants.GETSTATIC));
    120 il.append(factory.createInvoke("java.io.InputStreamReader", "&lt;init&gt;",
    121                                 Type.VOID, new Type[] { i_stream },
    122                                 Constants.INVOKESPECIAL));
    123 il.append(factory.createInvoke("java.io.BufferedReader", "&lt;init&gt;", Type.VOID,
    124                                 new Type[] {new ObjectType("java.io.Reader")},
    125                                 Constants.INVOKESPECIAL));
    126 
    127 LocalVariableGen lg = mg.addLocalVariable("in",
    128                         new ObjectType("java.io.BufferedReader"), null, null);
    129 int in = lg.getIndex();
    130 lg.setStart(il.append(new ASTORE(in))); // "i" valid from here
    131       </source>
    132 
    133       <p>
    134         Create local variable <tt>name</tt> and  initialize it to <tt>null</tt>.
    135       </p>
    136 
    137       <source>
    138 lg = mg.addLocalVariable("name", Type.STRING, null, null);
    139 int name = lg.getIndex();
    140 il.append(InstructionConstants.ACONST_NULL);
    141 lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
    142       </source>
    143 
    144       <p><b>Create try-catch block:</b> We remember the start of the
    145         block, read a line from the standard input and store it into the
    146         variable <tt>name</tt>.
    147       </p>
    148 
    149       <source>
    150 InstructionHandle try_start =
    151   il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC));
    152 
    153 il.append(new PUSH(cp, "Please enter your name&gt; "));
    154 il.append(factory.createInvoke("java.io.PrintStream", "print", Type.VOID,
    155                                 new Type[] { Type.STRING },
    156                                 Constants.INVOKEVIRTUAL));
    157 il.append(new ALOAD(in));
    158 il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
    159                                 Type.STRING, Type.NO_ARGS,
    160                                 Constants.INVOKEVIRTUAL));
    161 il.append(new ASTORE(name));
    162       </source>
    163 
    164       <p>
    165         Upon normal execution we jump behind exception handler, the target
    166         address is not known yet.
    167       </p>
    168 
    169       <source>
    170 GOTO g = new GOTO(null);
    171 InstructionHandle try_end = il.append(g);
    172       </source>
    173 
    174       <p>
    175         We add the exception handler which simply returns from the method.
    176       </p>
    177 
    178       <source>
    179 InstructionHandle handler = il.append(InstructionConstants.RETURN);
    180 mg.addExceptionHandler(try_start, try_end, handler, "java.io.IOException");
    181       </source>
    182 
    183       <p>
    184         "Normal" code continues, now we can set the branch target of the <tt>GOTO</tt>.
    185       </p>
    186 
    187       <source>
    188 InstructionHandle ih =
    189   il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC));
    190 g.setTarget(ih);
    191       </source>
    192 
    193       <p><b>Printing "Hello":</b>
    194 String concatenation compiles to <tt>StringBuffer</tt> operations.
    195       </p>
    196 
    197       <source>
    198 il.append(factory.createNew(Type.STRINGBUFFER));
    199 il.append(InstructionConstants.DUP);
    200 il.append(new PUSH(cp, "Hello, "));
    201 il.append(factory.createInvoke("java.lang.StringBuffer", "&lt;init&gt;",
    202                                 Type.VOID, new Type[] { Type.STRING },
    203                                 Constants.INVOKESPECIAL));
    204 il.append(new ALOAD(name));
    205 il.append(factory.createInvoke("java.lang.StringBuffer", "append",
    206                                 Type.STRINGBUFFER, new Type[] { Type.STRING },
    207                                 Constants.INVOKEVIRTUAL));
    208 il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
    209                                 Type.STRING, Type.NO_ARGS,
    210                                 Constants.INVOKEVIRTUAL));
    211 
    212 il.append(factory.createInvoke("java.io.PrintStream", "println",
    213                                 Type.VOID, new Type[] { Type.STRING },
    214                                 Constants.INVOKEVIRTUAL));
    215 il.append(InstructionConstants.RETURN);
    216       </source>
    217 
    218 
    219       <p><b>Finalization:</b> Finally, we have to set the stack size,
    220         which normally would have to be computed on the fly and add a
    221         default constructor method to the class, which is empty in this
    222         case.
    223       </p>
    224 
    225       <source>
    226 mg.setMaxStack();
    227 cg.addMethod(mg.getMethod());
    228 il.dispose(); // Allow instruction handles to be reused
    229 cg.addEmptyConstructor(ACC_PUBLIC);
    230       </source>
    231 
    232       <p>
    233         Last but not least we dump the <tt>JavaClass</tt> object to a file.
    234       </p>
    235 
    236       <source>
    237 try {
    238     cg.getJavaClass().dump("HelloWorld.class");
    239 } catch (IOException e) {
    240     System.err.println(e);
    241 }
    242       </source>
    243 
    244     </subsection>
    245 
    246     <subsection name="Peephole optimizer">
    247       <p>
    248         This class implements a simple peephole optimizer that removes any NOP
    249         instructions from the given class.
    250       </p>
    251 
    252       <source>
    253 import java.io.*;
    254 
    255 import java.util.Iterator;
    256 import org.apache.bcel.classfile.*;
    257 import org.apache.bcel.generic.*;
    258 import org.apache.bcel.Repository;
    259 import org.apache.bcel.util.InstructionFinder;
    260 
    261 public class Peephole {
    262 
    263     public static void main(String[] argv) {
    264         try {
    265             // Load the class from CLASSPATH.
    266             JavaClass clazz  = Repository.lookupClass(argv[0]);
    267             Method[] methods = clazz.getMethods();
    268             ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
    269     
    270             for (int i = 0; i &lt; methods.length; i++) {
    271                 if (!(methods[i].isAbstract() || methods[i].isNative())) {
    272                     MethodGen mg = new MethodGen(methods[i], clazz.getClassName(), cp);
    273                     Method stripped = removeNOPs(mg);
    274         
    275                     if (stripped != null)      // Any NOPs stripped?
    276                         methods[i] = stripped; // Overwrite with stripped method
    277                   }
    278             }
    279     
    280             // Dump the class to "class name"_.class
    281             clazz.setConstantPool(cp.getFinalConstantPool());
    282             clazz.dump(clazz.getClassName() + "_.class");
    283         } catch (Exception e) {
    284             e.printStackTrace();
    285         }
    286     }
    287 
    288     private static Method removeNOPs(MethodGen mg) {
    289         InstructionList il = mg.getInstructionList();
    290         InstructionFinder f = new InstructionFinder(il);
    291         String pat = "NOP+"; // Find at least one NOP
    292         InstructionHandle next = null;
    293         int count = 0;
    294         
    295         for (Iterator iter = f.search(pat); iter.hasNext();) {
    296             InstructionHandle[] match = (InstructionHandle[]) iter.next();
    297             InstructionHandle first = match[0];
    298             InstructionHandle last  = match[match.length - 1];
    299             
    300             // Some nasty Java compilers may add NOP at end of method.
    301             if ((next = last.getNext()) == null) {
    302                 break;
    303             }
    304             
    305             count += match.length;
    306             
    307             /**
    308              * Delete NOPs and redirect any references to them to the following (non-nop) instruction.
    309              */
    310             try {
    311                 il.delete(first, last);
    312             } catch (TargetLostException e) {
    313                 for (InstructionHandle target : e.getTargets()) {
    314                     for (InstructionTargeter targeter = target.getTargeters()) {
    315                         targeter.updateTarget(target, next);
    316                     }
    317                 }
    318             }
    319         }
    320 
    321         Method m = null;
    322 
    323         if (count &gt; 0) {
    324             System.out.println("Removed " + count + " NOP instructions from method " + mg.getName());
    325             m = mg.getMethod();
    326         }
    327 
    328         il.dispose(); // Reuse instruction handles
    329         return m;
    330     }
    331 }
    332       </source>
    333     </subsection>
    334 
    335     <subsection name="BCELifier">
    336       <p>
    337         If you want to learn how certain things are generated using BCEL you
    338         can do the following: Write your program with the needed features in
    339         Java and compile it as usual. Then use <tt>BCELifier</tt> to create
    340         a class that creates that very input class using BCEL.<br/>
    341         (Think about this sentence for a while, or just try it ...)
    342       </p>
    343     </subsection>
    344 
    345     <subsection name="Constant pool UML diagram">
    346 
    347       <p align="center">
    348         <a name="Figure 8">
    349           <img src="../images/constantpool.gif"/>
    350           <br/>
    351           Figure 8: UML diagram for constant pool classes
    352         </a>
    353       </p>
    354     </subsection>
    355       
    356     <subsection name="Verifier">
    357       
    358       <h4>Running a console based verifier</h4>
    359 
    360       <source>
    361 java org.apache.bcel.verifier.Verifier fully.qualified.class.Name          
    362       </source>
    363 
    364       lets JustIce work standalone.
    365       If you get a "java.lang.OutOfMemoryError", you should increase the
    366       maximum Java heap space. A command like
    367 
    368       <source>
    369 java -Xmx1887436800 org.apache.bcel.verifier.Verifier f.q.c.Name
    370       </source>
    371 
    372       will usually resolve the problem. The value above is suitable for
    373       big server machines; if your machine starts swapping to disk, try
    374       to lower the value.
    375 
    376       <h4>Running a graphics based verifier</h4>
    377 
    378       If you prefer a graphical application, you should use a command like
    379 
    380       <source>
    381 java org.apache.bcel.verifier.GraphicalVerifier
    382       </source>
    383 
    384       to launch one. Again, you may have to resolve a memory issue depending
    385       on the classes to verify.
    386     </subsection>
    387   </section>
    388   </body>
    389 </document>