1<html> 2<head> 3 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> 4 <title>Javassist Tutorial</title> 5 <link rel="stylesheet" type="text/css" href="brown.css"> 6</head> 7 8<body> 9 10<div align="right">Getting Started with Javassist</div> 11 12<div align="left"><a href="tutorial2.html">Previous page</a></div> 13 14<p> 15<a href="#intro">5. Bytecode level API</a> 16<ul> 17<li><a href="#classfile">Obtaining a <code>ClassFile</code> object</a> 18<br><li><a href="#member">Adding and removing a member</a> 19<br><li><a href="#traverse">Traversing a method body</a> 20<br><li><a href="#bytecode">Producing a bytecode sequence</a> 21<br><li><a href="#annotation">Annotations (Meta tags)</a> 22 23</ul> 24 25<p><a href="#generics">6. Generics</a> 26 27<p><a href="#varargs">7. Varargs</a> 28 29<p><a href="#j2me">8. J2ME</a> 30 31<p><a href="#boxing">9. Boxing/Unboxing 32 33<p><a href="#debug">10. Debug</a> 34 35<p><br> 36 37<a name="intro"> 38<h2>5. Bytecode level API</h2> 39 40<p> 41Javassist also provides lower-level API for directly editing 42a class file. To use this level of API, you need detailed 43knowledge of the Java bytecode and the class file format 44while this level of API allows you any kind of modification 45of class files. 46 47<p> 48If you want to just produce a simple class file, 49<code>javassist.bytecode.ClassFileWriter</code> might provide 50the best API for you. It is much faster than 51<code>javassist.bytecode.ClassFile</code> although its API 52is minimum. 53 54<a name="classfile"> 55<h3>5.1 Obtaining a <code>ClassFile</code> object</h3> 56 57<p>A <code>javassist.bytecode.ClassFile</code> object represents 58a class file. To obtian this object, <code>getClassFile()</code> 59in <code>CtClass</code> should be called. 60 61<p>Otherwise, you can construct a 62<code>javassist.bytecode.ClassFile</code> directly from a class file. 63For example, 64 65<ul><pre> 66BufferedInputStream fin 67 = new BufferedInputStream(new FileInputStream("Point.class")); 68ClassFile cf = new ClassFile(new DataInputStream(fin)); 69</pre></ul> 70 71<p> 72This code snippet creats a <code>ClassFile</code> object from 73<code>Point.class</code>. 74 75<p> 76A <code>ClassFile</code> object can be written back to a 77class file. <code>write()</code> in <code>ClassFile</code> 78writes the contents of the class file to a given 79<code>DataOutputStream</code>. 80 81<p>You can create a new class file from scratch. For example, 82<blockquote><pre> 83ClassFile cf = new ClassFile(false, "test.Foo", null); 84cf.setInterfaces(new String[] { "java.lang.Cloneable" }); 85 86FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I"); 87f.setAccessFlags(AccessFlag.PUBLIC); 88cf.addField(f); 89 90cf.write(new DataOutputStream(new FileOutputStream("Foo.class"))); 91</pre></blockquote> 92 93<p>this code generates a class file <code>Foo.class</code> that contains 94the implementation of the following class: 95 96<blockquote><pre> 97package test; 98class Foo implements Cloneable { 99 public int width; 100} 101</pre></blockquote> 102 103<p><br> 104 105<a name="member"> 106<h3>5.2 Adding and removing a member</h3> 107 108<p> 109<code>ClassFile</code> provides <code>addField()</code> and 110<code>addMethod()</code> for adding a field or a method (note that 111a constructor is regarded as a method at the bytecode level). 112It also provides <code>addAttribute()</code> for adding an attribute 113to the class file. 114 115<p> 116Note that <code>FieldInfo</code>, <code>MethodInfo</code>, and 117<code>AttributeInfo</code> objects include a link to a 118<code>ConstPool</code> (constant pool table) object. The <code>ConstPool</code> 119object must be common to the <code>ClassFile</code> object and 120a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object 121that is added to that <code>ClassFile</code> object. 122In other words, a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object 123must not be shared among different <code>ClassFile</code> objects. 124 125<p> 126To remove a field or a method from a <code>ClassFile</code> object, 127you must first obtain a <code>java.util.List</code> 128object containing all the fields of the class. <code>getFields()</code> 129and <code>getMethods()</code> return the lists. A field or a method can 130be removed by calling <code>remove()</code> on the <code>List</code> object. 131An attribute can be removed in a similar way. 132Call <code>getAttributes()</code> in <code>FieldInfo</code> or 133<code>MethodInfo</code> to obtain the list of attributes, 134and remove one from the list. 135 136 137<p><br> 138 139<a name="traverse"> 140<h3>5.3 Traversing a method body</h3> 141 142<p> 143To examine every bytecode instruction in a method body, 144<code>CodeIterator</code> is useful. To otbain this object, 145do as follows: 146 147<ul><pre> 148ClassFile cf = ... ; 149MethodInfo minfo = cf.getMethod("move"); // we assume move is not overloaded. 150CodeAttribute ca = minfo.getCodeAttribute(); 151CodeIterator i = ca.iterator(); 152</pre></ul> 153 154<p> 155A <code>CodeIterator</code> object allows you to visit every 156bytecode instruction one by one from the beginning to the end. 157The following methods are part of the methods declared in 158<code>CodeIterator</code>: 159 160<ul> 161<li><code>void begin()</code><br> 162Move to the first instruction.<br> 163<li><code>void move(int index)</code><br> 164Move to the instruction specified by the given index.<br> 165<li><code>boolean hasNext()</code><br> 166Returns true if there is more instructions.<br> 167<li><code>int next()</code><br> 168Returns the index of the next instruction.<br> 169<em>Note that it does not return the opcode of the next 170instruction.</em><br> 171<li><code>int byteAt(int index)</code><br> 172Returns the unsigned 8bit value at the index.<br> 173<li><code>int u16bitAt(int index)</code><br> 174Returns the unsigned 16bit value at the index.<br> 175<li><code>int write(byte[] code, int index)</code><br> 176Writes a byte array at the index.<br> 177<li><code>void insert(int index, byte[] code)</code><br> 178Inserts a byte array at the index. 179Branch offsets etc. are automatically adjusted.<br> 180</ul> 181 182<p>The following code snippet displays all the instructions included 183in a method body: 184 185<ul><pre> 186CodeIterator ci = ... ; 187while (ci.hasNext()) { 188 int index = ci.next(); 189 int op = ci.byteAt(index); 190 System.out.println(Mnemonic.OPCODE[op]); 191} 192</pre></ul> 193 194<p><br> 195 196<a name="bytecode"> 197<h3>5.4 Producing a bytecode sequence</h3> 198 199<p> 200A <code>Bytecode</code> object represents a sequence of bytecode 201instructions. It is a growable array of bytecode. 202Here is a sample code snippet: 203 204<ul><pre> 205ConstPool cp = ...; // constant pool table 206Bytecode b = new Bytecode(cp, 1, 0); 207b.addIconst(3); 208b.addReturn(CtClass.intType); 209CodeAttribute ca = b.toCodeAttribute(); 210</pre></ul> 211 212<p> 213This produces the code attribute representing the following sequence: 214 215<ul><pre> 216iconst_3 217ireturn 218</pre></ul> 219 220<p> 221You can also obtain a byte array containing this sequence by 222calling <code>get()</code> in <code>Bytecode</code>. The 223obtained array can be inserted in another code attribute. 224 225<p> 226While <code>Bytecode</code> provides a number of methods for adding a 227specific instruction to the sequence, it provides 228<code>addOpcode()</code> for adding an 8bit opcode and 229<code>addIndex()</code> for adding an index. 230The 8bit value of each opcode is defined in the <code>Opcode</code> 231interface. 232 233<p> 234<code>addOpcode()</code> and other methods for adding a specific 235instruction are automatically maintain the maximum stack depth 236unless the control flow does not include a branch. 237This value can be obtained by calling <code>getMaxStack()</code> 238on the <code>Bytecode</code> object. 239It is also reflected on the <code>CodeAttribute</code> object 240constructed from the <code>Bytecode</code> object. 241To recompute the maximum stack depth of a method body, 242call <code>computeMaxStack()</code> in <code>CodeAttribute</code>. 243 244<p><code>Bytecode</code> can be used to construct a method. 245For example, 246 247<blockquote><pre> 248ClassFile cf = ... 249Bytecode code = new Bytecode(cf.getConstPool()); 250code.addAload(0); 251code.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V"); 252code.addReturn(null); 253code.setMaxLocals(1); 254 255MethodInfo minfo = new MethodInfo(cf.getConstPool(), MethodInfo.nameInit, "()V"); 256minfo.setCodeAttribute(code.toCodeAttribute()); 257cf.addMethod(minfo); 258</pre></blockquote> 259 260<p>this code makes the default constructor and adds it to the class specified 261by <code>cf</code>. The <code>Bytecode</code> object is first converted into 262a <code>CodeAttribute</code> object and then added to the method specified 263by <code>minfo</code>. The method is finally added to a class file <code>cf</code>. 264 265<p><br> 266 267<a name="annotation"> 268<h3>5.5 Annotations (Meta tags)</h3> 269 270<p>Annotations are stored in a class file 271as runtime invisible (or visible) annotations attribute. 272These attributes can be obtained from <code>ClassFile</code>, 273<code>MethodInfo</code>, or <code>FieldInfo</code> objects. 274Call <code>getAttribute(AnnotationsAttribute.invisibleTag)</code> 275on those objects. For more details, see the javadoc manual 276of <code>javassist.bytecode.AnnotationsAttribute</code> class 277and the <code>javassist.bytecode.annotation</code> package. 278 279<p>Javassist also let you access annotations by the higher-level 280API. 281If you want to access annotations through <code>CtClass</code>, 282call <code>getAnnotations()</code> in <code>CtClass</code> or 283<code>CtBehavior</code>. 284 285<p><br> 286 287<h2><a name="generics">6. Generics</a></h2> 288 289<p>The lower-level API of Javassist fully supports generics 290introduced by Java 5. On the other hand, the higher-level 291API such as <code>CtClass</code> does not directly support 292generics. However, this is not a serious problem for bytecode 293transformation. 294 295<p>The generics of Java is implemented by the erasure technique. 296After compilation, all type parameters are dropped off. For 297example, suppose that your source code declares a parameterized 298type <code>Vector<String></code>: 299 300<ul><pre> 301Vector<String> v = new Vector<String>(); 302 : 303String s = v.get(0); 304</pre></ul> 305 306<p>The compiled bytecode is equivalent to the following code: 307 308<ul><pre> 309Vector v = new Vector(); 310 : 311String s = (String)v.get(0); 312</pre></ul> 313 314<p>So when you write a bytecode transformer, you can just drop 315off all type parameters. Because the compiler embedded in Javassist 316does not support generics, 317you must insert an explicit type cast at the 318caller site if the source code is compiled by Javassist, for example, 319through <code>CtMethod.make()</code>. No type cast 320is necessary if the source code is compiled by a normal Java compiler 321such as <code>javac</code>. 322 323<p>For example, if you have a class: 324 325<ul><pre> 326public class Wrapper<T> { 327 T value; 328 public Wrapper(T t) { value = t; } 329} 330</pre></ul> 331 332<p>and want to add an interface <code>Getter<T></code> to the 333class <code>Wrapper<T></code>: 334 335<ul><pre> 336public interface Getter<T> { 337 T get(); 338} 339</pre></ul> 340 341<p>then the interface you really have to add is <code>Getter</code> 342(the type parameters <code><T></code> drops off) 343and the method you also have to add to the <code>Wrapper</code> 344class is this simple one: 345 346<ul><pre> 347public Object get() { return value; } 348</pre></ul> 349 350<p>Note that no type parameters are necessary. 351Since <code>get</code> returns an <code>Object</code>, an explicit type cast 352is needed at the caller site if the source code is compiled by Javassist. 353For example, if the type parameter <code>T</code> 354is <code>String</code>, then <code>(String)</code> must be inserted as follows: 355 356<ul><pre> 357Wrapper w = ... 358String s = (String)w.get(); 359</pre></ul> 360 361<p>The type cast is not needed if the source code is compiled by a normal Java 362compiler because it will automatically insert a type cast. 363 364<p>If you need to make type parameters accessible through reflection 365during runtime, you have to add generic signatures to the class file. 366For more details, see the API documentation (javadoc) of the 367<code>setGenericSignature</code> method in the <code>CtClass</code>. 368 369<p><br> 370 371<h2><a name="varargs">7. Varargs</a></h2> 372 373<p>Currently, Javassist does not directly support varargs. So to make a method with varargs, 374you must explicitly set a method modifier. But this is easy. 375Suppose that now you want to make the following method: 376 377<ul><pre> 378public int length(int... args) { return args.length; } 379</pre></ul> 380 381<p>The following code using Javassist will make the method shown above: 382 383<ul><pre> 384CtClass cc = /* target class */; 385CtMethod m = CtMethod.make("public int length(int[] args) { return args.length; }", cc); 386m.setModifiers(m.getModifiers() | Modifier.VARARGS); 387cc.addMethod(m); 388<pre></ul> 389 390<p>The parameter type <code>int...</code> is changed into <code>int[]</code> 391and <code>Modifier.VARARGS</code> is added to the method modifiers. 392 393<p>To call this method in the source code compiled by the compiler embedded in Javassist, 394you must write: 395 396<ul><pre> 397length(new int[] { 1, 2, 3 }); 398</pre></ul> 399 400<p>instead of this method call using the varargs mechanism: 401 402<ul><pre> 403length(1, 2, 3); 404</pre></ul> 405 406<p><br> 407 408<h2><a name="j2me">8. J2ME</a></h2> 409 410<p>If you modify a class file for the J2ME execution environment, 411you must perform <it>preverification</it>. Preverifying is basically 412producing stack maps, which is similar to stack map tables introduced 413into J2SE at JDK 1.6. Javassist maintains the stack maps for J2ME only if 414<code>javassist.bytecode.MethodInfo.doPreverify</code> is true. 415 416<p>You can also manually 417produce a stack map for a modified method. 418For a given method represented by a <code>CtMethod</code> object <code>m</code>, 419you can produce a stack map by calling the following methods: 420 421<ul><pre> 422m.getMethodInfo().rebuildStackMapForME(cpool); 423</pre></ul> 424 425<p>Here, <code>cpool</code> is a <code>ClassPool</code> object, which is 426available by calling <code>getClassPool()</code> on a <code>CtClass</code> 427object. A <code>ClassPool</code> object is responsible for finding 428class files from given class pathes. To obtain all the <code>CtMethod</code> 429objects, call the <code>getDeclaredMethods</code> method on a <code>CtClass</code> object. 430 431<p><br> 432 433<h2><a name="boxing">9. Boxing/Unboxing</h2> 434 435<p>Boxing and unboxing in Java are syntactic sugar. There is no bytecode for 436boxing or unboxing. So the compiler of Javassist does not support them. 437For example, the following statement is valid in Java: 438 439<ul><pre> 440Integer i = 3; 441</pre></ul> 442 443<p>since boxing is implicitly performed. For Javassist, however, you must explicitly 444convert a value type from <code>int</code> to <code>Integer</code>: 445 446<ul><pre> 447Integer i = new Integer(3); 448</pre></ul> 449 450<p><br> 451 452<h2><a name="debug">10. Debug</h2> 453 454<p>Set <code>CtClass.debugDump</code> to a directory name. 455Then all class files modified and generated by Javassist are saved in that 456directory. To stop this, set <code>CtClass.debugDump</code> to null. 457The default value is null. 458 459<p>For example, 460 461<ul><pre> 462CtClass.debugDump = "./dump"; 463</pre></ul> 464 465<p>All modified class files are saved in <code>./dump</code>. 466 467<p><br> 468 469<a href="tutorial2.html">Previous page</a> 470 471<hr> 472Java(TM) is a trademark of Sun Microsystems, Inc.<br> 473Copyright (C) 2000-2015 by Shigeru Chiba, All rights reserved. 474</body> 475</html> 476