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