• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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&lt;String&gt;</code>:
299
300<ul><pre>
301Vector&lt;String&gt; v = new Vector&lt;String&gt();
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&lt;T&gt; {
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&lt;T&gt;</code> to the
333class <code>Wrapper&lt;T&gt;</code>:
334
335<ul><pre>
336public interface Getter&lt;T&gt; {
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>&lt;T&gt;</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