| <html> |
| <head> |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
| <title>Javassist Tutorial</title> |
| <link rel="stylesheet" type="text/css" href="brown.css"> |
| </head> |
| |
| <body> |
| |
| <div align="right">Getting Started with Javassist</div> |
| |
| <div align="left"><a href="tutorial2.html">Previous page</a></div> |
| |
| <p> |
| <a href="#intro">5. Bytecode level API</a> |
| <ul> |
| <li><a href="#classfile">Obtaining a <code>ClassFile</code> object</a> |
| <br><li><a href="#member">Adding and removing a member</a> |
| <br><li><a href="#traverse">Traversing a method body</a> |
| <br><li><a href="#bytecode">Producing a bytecode sequence</a> |
| <br><li><a href="#annotation">Annotations (Meta tags)</a> |
| |
| </ul> |
| |
| <p><a href="#generics">6. Generics</a> |
| |
| <p><a href="#varargs">7. Varargs</a> |
| |
| <p><a href="#j2me">8. J2ME</a> |
| |
| <p><br> |
| |
| <a name="intro"> |
| <h2>5. Bytecode level API</h2> |
| |
| <p> |
| Javassist also provides lower-level API for directly editing |
| a class file. To use this level of API, you need detailed |
| knowledge of the Java bytecode and the class file format |
| while this level of API allows you any kind of modification |
| of class files. |
| |
| <p> |
| If you want to just produce a simple class file, |
| <code>javassist.bytecode.ClassFileWriter</code> might provide |
| the best API for you. It is much faster than |
| <code>javassist.bytecode.ClassFile</code> although its API |
| is minimum. |
| |
| <a name="classfile"> |
| <h3>5.1 Obtaining a <code>ClassFile</code> object</h3> |
| |
| <p>A <code>javassist.bytecode.ClassFile</code> object represents |
| a class file. To obtian this object, <code>getClassFile()</code> |
| in <code>CtClass</code> should be called. |
| |
| <p>Otherwise, you can construct a |
| <code>javassist.bytecode.ClassFile</code> directly from a class file. |
| For example, |
| |
| <ul><pre> |
| BufferedInputStream fin |
| = new BufferedInputStream(new FileInputStream("Point.class")); |
| ClassFile cf = new ClassFile(new DataInputStream(fin)); |
| </pre></ul> |
| |
| <p> |
| This code snippet creats a <code>ClassFile</code> object from |
| <code>Point.class</code>. |
| |
| <p> |
| A <code>ClassFile</code> object can be written back to a |
| class file. <code>write()</code> in <code>ClassFile</code> |
| writes the contents of the class file to a given |
| <code>DataOutputStream</code>. |
| |
| <p><br> |
| |
| <a name="member"> |
| <h3>5.2 Adding and removing a member</h3> |
| |
| <p> |
| <code>ClassFile</code> provides <code>addField()</code> and |
| <code>addMethod()</code> for adding a field or a method (note that |
| a constructor is regarded as a method at the bytecode level). |
| It also provides <code>addAttribute()</code> for adding an attribute |
| to the class file. |
| |
| <p> |
| Note that <code>FieldInfo</code>, <code>MethodInfo</code>, and |
| <code>AttributeInfo</code> objects include a link to a |
| <code>ConstPool</code> (constant pool table) object. The <code>ConstPool</code> |
| object must be common to the <code>ClassFile</code> object and |
| a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object |
| that is added to that <code>ClassFile</code> object. |
| In other words, a <code>FieldInfo</code> (or <code>MethodInfo</code> etc.) object |
| must not be shared among different <code>ClassFile</code> objects. |
| |
| <p> |
| To remove a field or a method from a <code>ClassFile</code> object, |
| you must first obtain a <code>java.util.List</code> |
| object containing all the fields of the class. <code>getFields()</code> |
| and <code>getMethods()</code> return the lists. A field or a method can |
| be removed by calling <code>remove()</code> on the <code>List</code> object. |
| An attribute can be removed in a similar way. |
| Call <code>getAttributes()</code> in <code>FieldInfo</code> or |
| <code>MethodInfo</code> to obtain the list of attributes, |
| and remove one from the list. |
| |
| |
| <p><br> |
| |
| <a name="traverse"> |
| <h3>5.3 Traversing a method body</h3> |
| |
| <p> |
| To examine every bytecode instruction in a method body, |
| <code>CodeIterator</code> is useful. To otbain this object, |
| do as follows: |
| |
| <ul><pre> |
| ClassFile cf = ... ; |
| MethodInfo minfo = cf.getMethod("move"); // we assume move is not overloaded. |
| CodeAttribute ca = minfo.getCodeAttribute(); |
| CodeIterator i = ca.iterator(); |
| </pre></ul> |
| |
| <p> |
| A <code>CodeIterator</code> object allows you to visit every |
| bytecode instruction one by one from the beginning to the end. |
| The following methods are part of the methods declared in |
| <code>CodeIterator</code>: |
| |
| <ul> |
| <li><code>void begin()</code><br> |
| Move to the first instruction.<br> |
| <li><code>void move(int index)</code><br> |
| Move to the instruction specified by the given index.<br> |
| <li><code>boolean hasNext()</code><br> |
| Returns true if there is more instructions.<br> |
| <li><code>int next()</code><br> |
| Returns the index of the next instruction.<br> |
| <em>Note that it does not return the opcode of the next |
| instruction.</em><br> |
| <li><code>int byteAt(int index)</code><br> |
| Returns the unsigned 8bit value at the index.<br> |
| <li><code>int u16bitAt(int index)</code><br> |
| Returns the unsigned 16bit value at the index.<br> |
| <li><code>int write(byte[] code, int index)</code><br> |
| Writes a byte array at the index.<br> |
| <li><code>void insert(int index, byte[] code)</code><br> |
| Inserts a byte array at the index. |
| Branch offsets etc. are automatically adjusted.<br> |
| </ul> |
| |
| <p>The following code snippet displays all the instructions included |
| in a method body: |
| |
| <ul><pre> |
| CodeIterator ci = ... ; |
| while (ci.hasNext()) { |
| int index = ci.next(); |
| int op = ci.byteAt(index); |
| System.out.println(Mnemonic.OPCODE[op]); |
| } |
| </pre></ul> |
| |
| <p><br> |
| |
| <a name="bytecode"> |
| <h3>5.4 Producing a bytecode sequence</h3> |
| |
| <p> |
| A <code>Bytecode</code> object represents a sequence of bytecode |
| instructions. It is a growable array of bytecode. |
| Here is a sample code snippet: |
| |
| <ul><pre> |
| ConstPool cp = ...; // constant pool table |
| Bytecode b = new Bytecode(cp, 1, 0); |
| b.addIconst(3); |
| b.addReturn(CtClass.intType); |
| CodeAttribute ca = b.toCodeAttribute(); |
| </pre></ul> |
| |
| <p> |
| This produces the code attribute representing the following sequence: |
| |
| <ul><pre> |
| iconst_3 |
| ireturn |
| </pre></ul> |
| |
| <p> |
| You can also obtain a byte array containing this sequence by |
| calling <code>get()</code> in <code>Bytecode</code>. The |
| obtained array can be inserted in another code attribute. |
| |
| <p> |
| While <code>Bytecode</code> provides a number of methods for adding a |
| specific instruction to the sequence, it provides |
| <code>addOpcode()</code> for adding an 8bit opcode and |
| <code>addIndex()</code> for adding an index. |
| The 8bit value of each opcode is defined in the <code>Opcode</code> |
| interface. |
| |
| <p> |
| <code>addOpcode()</code> and other methods for adding a specific |
| instruction are automatically maintain the maximum stack depth |
| unless the control flow does not include a branch. |
| This value can be obtained by calling <code>getMaxStack()</code> |
| on the <code>Bytecode</code> object. |
| It is also reflected on the <code>CodeAttribute</code> object |
| constructed from the <code>Bytecode</code> object. |
| To recompute the maximum stack depth of a method body, |
| call <code>computeMaxStack()</code> in <code>CodeAttribute</code>. |
| |
| <p><br> |
| |
| <a name="annotation"> |
| <h3>5.5 Annotations (Meta tags)</h3> |
| |
| <p>Annotations are stored in a class file |
| as runtime invisible (or visible) annotations attribute. |
| These attributes can be obtained from <code>ClassFile</code>, |
| <code>MethodInfo</code>, or <code>FieldInfo</code> objects. |
| Call <code>getAttribute(AnnotationsAttribute.invisibleTag)</code> |
| on those objects. For more details, see the javadoc manual |
| of <code>javassist.bytecode.AnnotationsAttribute</code> class |
| and the <code>javassist.bytecode.annotation</code> package. |
| |
| <p>Javassist also let you access annotations by the higher-level |
| API. |
| If you want to access annotations through <code>CtClass</code>, |
| call <code>getAnnotations()</code> in <code>CtClass</code> or |
| <code>CtBehavior</code>. |
| |
| <p><br> |
| |
| <h2><a name="generics">6. Generics</a></h2> |
| |
| <p>The lower-level API of Javassist fully supports generics |
| introduced by Java 5. On the other hand, the higher-level |
| API such as <code>CtClass</code> does not directly support |
| generics. However, this is not a serious problem for bytecode |
| transformation. |
| |
| <p>The generics of Java is implemented by the erasure technique. |
| After compilation, all type parameters are dropped off. For |
| example, suppose that your source code declares a parameterized |
| type <code>Vector<String></code>: |
| |
| <ul><pre> |
| Vector<String> v = new Vector<String>(); |
| : |
| String s = v.get(0); |
| </pre></ul> |
| |
| <p>The compiled bytecode is equivalent to the following code: |
| |
| <ul><pre> |
| Vector v = new Vector(); |
| : |
| String s = (String)v.get(0); |
| </pre></ul> |
| |
| <p>So when you write a bytecode transformer, you can just drop |
| off all type parameters. For example, if you have a class: |
| |
| <ul><pre> |
| public class Wrapper<T> { |
| T value; |
| public Wrapper(T t) { value = t; } |
| } |
| </pre></ul> |
| |
| <p>and want to add an interface <code>Getter<T></code> to the |
| class <code>Wrapper<T></code>: |
| |
| <ul><pre> |
| public interface Getter<T> { |
| T get(); |
| } |
| </pre></ul> |
| |
| <p>Then the interface you really have to add is <code>Getter</code> |
| (the type parameters <code><T></code> drops off) |
| and the method you also have to add to the <code>Wrapper</code> |
| class is this simple one: |
| |
| <ul><pre> |
| public Object get() { return value; } |
| </pre></ul> |
| |
| <p>Note that no type parameters are necessary. |
| |
| <p><br> |
| |
| <h2><a name="varargs">7. Varargs</a></h2> |
| |
| <p>Currently, Javassist does not directly support varargs. So to make a method with varargs, |
| you must explicitly set a method modifier. But this is easy. |
| Suppose that now you want to make the following method: |
| |
| <ul><pre> |
| public int length(int... args) { return args.length; } |
| </pre></ul> |
| |
| <p>The following code using Javassist will make the method shown above: |
| |
| <ul><pre> |
| CtClass cc = /* target class */; |
| CtMethod m = CtMethod.make("public int length(int[] args) { return args.length; }", cc); |
| m.setModifiers(m.getModifiers() | Modifier.VARARGS); |
| cc.addMethod(m); |
| <pre></ul> |
| |
| <p>The parameter type <code>int...</code> is changed into <code>int[]</code> |
| and <code>Modifier.VARARGS</code> is added to the method modifiers. |
| |
| <p>To call this method, you must write: |
| |
| <ul><pre> |
| length(new int[] { 1, 2, 3 }); |
| </pre></ul> |
| |
| <p>instead of this method call using the varargs mechanism: |
| |
| <ul><pre> |
| length(1, 2, 3); |
| </pre></ul> |
| |
| <p><br> |
| |
| <h2><a name="j2me">8. J2ME</a></h2> |
| |
| <p>If you modify a class file for the J2ME execution environment, |
| you must perform <it>preverification</it>. Preverifying is basically |
| producing stack maps, which is similar to stack map tables introduced |
| into J2SE at JDK 1.6. Javassist maintains the stack maps for J2ME only if |
| <code>javassist.bytecode.MethodInfo.doPreverify</code> is true. |
| |
| <p>You can also manually |
| produce a stack map for a modified method. |
| For a given method represented by a <code>CtMethod</code> object <code>m</code>, |
| you can produce a stack map by calling the following methods: |
| |
| <ul><pre> |
| m.getMethodInfo().rebuildStackMapForME(cpool); |
| </pre></ul> |
| |
| <p>Here, <code>cpool</code> is a <code>ClassPool</code> object, which is |
| available by calling <code>getClassPool()</code> on a <code>CtClass</code> |
| object. A <code>ClassPool</code> object is responsible for finding |
| class files from given class pathes. To obtain all the <code>CtMethod</code> |
| objects, call the <code>getDeclaredMethods</code> method on a <code>CtClass</code> object. |
| |
| <p><br> |
| |
| <a href="tutorial2.html">Previous page</a> |
| |
| <hr> |
| Java(TM) is a trademark of Sun Microsystems, Inc.<br> |
| Copyright (C) 2000-2010 by Shigeru Chiba, All rights reserved. |
| </body> |
| </html> |