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> 37import java.io.*; 38 39public 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> "); 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> 76ClassGen cg = new ClassGen("HelloWorld", "java.lang.Object", 77 "<generated>", ACC_PUBLIC | ACC_SUPER, null); 78ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool 79InstructionList 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> 88MethodGen 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); 95InstructionFactory factory = new InstructionFactory(cg); 96 </source> 97 98 <p> 99 We now define some often used types: 100 </p> 101 102 <source> 103ObjectType i_stream = new ObjectType("java.io.InputStream"); 104ObjectType 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> 115il.append(factory.createNew("java.io.BufferedReader")); 116il.append(InstructionConstants.DUP); // Use predefined constant 117il.append(factory.createNew("java.io.InputStreamReader")); 118il.append(InstructionConstants.DUP); 119il.append(factory.createFieldAccess("java.lang.System", "in", i_stream, Constants.GETSTATIC)); 120il.append(factory.createInvoke("java.io.InputStreamReader", "<init>", 121 Type.VOID, new Type[] { i_stream }, 122 Constants.INVOKESPECIAL)); 123il.append(factory.createInvoke("java.io.BufferedReader", "<init>", Type.VOID, 124 new Type[] {new ObjectType("java.io.Reader")}, 125 Constants.INVOKESPECIAL)); 126 127LocalVariableGen lg = mg.addLocalVariable("in", 128 new ObjectType("java.io.BufferedReader"), null, null); 129int in = lg.getIndex(); 130lg.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> 138lg = mg.addLocalVariable("name", Type.STRING, null, null); 139int name = lg.getIndex(); 140il.append(InstructionConstants.ACONST_NULL); 141lg.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> 150InstructionHandle try_start = 151 il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC)); 152 153il.append(new PUSH(cp, "Please enter your name> ")); 154il.append(factory.createInvoke("java.io.PrintStream", "print", Type.VOID, 155 new Type[] { Type.STRING }, 156 Constants.INVOKEVIRTUAL)); 157il.append(new ALOAD(in)); 158il.append(factory.createInvoke("java.io.BufferedReader", "readLine", 159 Type.STRING, Type.NO_ARGS, 160 Constants.INVOKEVIRTUAL)); 161il.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> 170GOTO g = new GOTO(null); 171InstructionHandle 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> 179InstructionHandle handler = il.append(InstructionConstants.RETURN); 180mg.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> 188InstructionHandle ih = 189 il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC)); 190g.setTarget(ih); 191 </source> 192 193 <p><b>Printing "Hello":</b> 194String concatenation compiles to <tt>StringBuffer</tt> operations. 195 </p> 196 197 <source> 198il.append(factory.createNew(Type.STRINGBUFFER)); 199il.append(InstructionConstants.DUP); 200il.append(new PUSH(cp, "Hello, ")); 201il.append(factory.createInvoke("java.lang.StringBuffer", "<init>", 202 Type.VOID, new Type[] { Type.STRING }, 203 Constants.INVOKESPECIAL)); 204il.append(new ALOAD(name)); 205il.append(factory.createInvoke("java.lang.StringBuffer", "append", 206 Type.STRINGBUFFER, new Type[] { Type.STRING }, 207 Constants.INVOKEVIRTUAL)); 208il.append(factory.createInvoke("java.lang.StringBuffer", "toString", 209 Type.STRING, Type.NO_ARGS, 210 Constants.INVOKEVIRTUAL)); 211 212il.append(factory.createInvoke("java.io.PrintStream", "println", 213 Type.VOID, new Type[] { Type.STRING }, 214 Constants.INVOKEVIRTUAL)); 215il.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> 226mg.setMaxStack(); 227cg.addMethod(mg.getMethod()); 228il.dispose(); // Allow instruction handles to be reused 229cg.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> 237try { 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> 253import java.io.*; 254 255import java.util.Iterator; 256import org.apache.bcel.classfile.*; 257import org.apache.bcel.generic.*; 258import org.apache.bcel.Repository; 259import org.apache.bcel.util.InstructionFinder; 260 261public 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 < 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 > 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> 361java 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> 369java -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> 381java 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>