KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javassist > CodeConverter


1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */

15
16 package javassist;
17
18 import javassist.bytecode.*;
19 import javassist.convert.*;
20
21 /**
22  * Simple translator of method bodies
23  * (also see the <code>javassist.expr</code> package).
24  *
25  * <p>Instances of this class specifies how to instrument of the
26  * bytecodes representing a method body. They are passed to
27  * <code>CtClass.instrument()</code> or
28  * <code>CtMethod.instrument()</code> as a parameter.
29  *
30  * <p>Example:
31  * <ul><pre>
32  * ClassPool cp = ClassPool.getDefault();
33  * CtClass point = cp.get("Point");
34  * CtClass singleton = cp.get("Singleton");
35  * CtClass client = cp.get("Client");
36  * CodeConverter conv = new CodeConverter();
37  * conv.replaceNew(point, singleton, "makePoint");
38  * client.instrument(conv);
39  * </pre></ul>
40  *
41  * <p>This program substitutes "<code>Singleton.makePoint()</code>"
42  * for all occurrences of "<code>new Point()</code>"
43  * appearing in methods declared in a <code>Client</code> class.
44  *
45  * @see javassist.CtClass#instrument(CodeConverter)
46  * @see javassist.CtMethod#instrument(CodeConverter)
47  * @see javassist.expr.ExprEditor
48  */

49 public class CodeConverter {
50     Transformer transformers = null;
51
52     /**
53      * Modify a method body so that instantiation of the specified class
54      * is replaced with a call to the specified static method. For example,
55      * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
56      * (where <code>ctPoint</code> and <code>ctSingleton</code> are
57      * compile-time classes for class <code>Point</code> and class
58      * <code>Singleton</code>, respectively)
59      * replaces all occurrences of:
60      *
61      * <ul><code>new Point(x, y)</code></ul>
62      *
63      * in the method body with:
64      *
65      * <ul><code>Singleton.createPoint(x, y)</code></ul>
66      *
67      * <p>This enables to intercept instantiation of <code>Point</code>
68      * and change the samentics. For example, the following
69      * <code>createPoint()</code> implements the singleton pattern:
70      *
71      * <ul><pre>public static Point createPoint(int x, int y) {
72      * if (aPoint == null)
73      * aPoint = new Point(x, y);
74      * return aPoint;
75      * }
76      * </pre></ul>
77      *
78      * <p>The static method call substituted for the original <code>new</code>
79      * expression must be
80      * able to receive the same set of parameters as the original
81      * constructor. If there are multiple constructors with different
82      * parameter types, then there must be multiple static methods
83      * with the same name but different parameter types.
84      *
85      * <p>The return type of the substituted static method must be
86      * the exactly same as the type of the instantiated class specified by
87      * <code>newClass</code>.
88      *
89      * @param newClass the instantiated class.
90      * @param calledClass the class in which the static method is
91      * declared.
92      * @param calledMethod the name of the static method.
93      */

94     public void replaceNew(CtClass newClass,
95                            CtClass calledClass, String JavaDoc calledMethod) {
96         transformers = new TransformNew(transformers, newClass.getName(),
97                                         calledClass.getName(), calledMethod);
98     }
99
100     /**
101      * Modify a method body so that field read/write expressions access
102      * a different field from the original one.
103      *
104      * <p>Note that this method changes only the filed name and the class
105      * declaring the field; the type of the target object does not change.
106      * Therefore, the substituted field must be declared in the same class
107      * or a superclass of the original class.
108      *
109      * <p>Also, <code>clazz</code> and <code>newClass</code> must specify
110      * the class directly declaring the field. They must not specify
111      * a subclass of that class.
112      *
113      * @param field the originally accessed field.
114      * @param newClass the class declaring the substituted field.
115      * @param newFieldname the name of the substituted field.
116      */

117     public void redirectFieldAccess(CtField field,
118                                     CtClass newClass, String JavaDoc newFieldname) {
119         transformers = new TransformFieldAccess(transformers, field,
120                                                 newClass.getName(),
121                                                 newFieldname);
122     }
123
124     /**
125      * Modify a method body so that an expression reading the specified
126      * field is replaced with a call to the specified <i>static</i> method.
127      * This static method receives the target object of the original
128      * read expression as a parameter. It must return a value of
129      * the same type as the field.
130      *
131      * <p>For example, the program below
132      *
133      * <ul><pre>Point p = new Point();
134      * int newX = p.x + 3;</pre></ul>
135      *
136      * <p>can be translated into:
137      *
138      * <ul><pre>Point p = new Point();
139      * int newX = Accessor.readX(p) + 3;</pre></ul>
140      *
141      * <p>where
142      *
143      * <ul><pre>public class Accessor {
144      * public static int readX(Object target) { ... }
145      * }</pre></ul>
146      *
147      * <p>The type of the parameter of <code>readX()</code> must
148      * be <code>java.lang.Object</code> independently of the actual
149      * type of <code>target</code>. The return type must be the same
150      * as the field type.
151      *
152      * @param field the field.
153      * @param calledClass the class in which the static method is
154      * declared.
155      * @param calledMethod the name of the static method.
156      */

157     public void replaceFieldRead(CtField field,
158                                  CtClass calledClass, String JavaDoc calledMethod) {
159         transformers = new TransformReadField(transformers, field,
160                                               calledClass.getName(),
161                                               calledMethod);
162     }
163
164     /**
165      * Modify a method body so that an expression writing the specified
166      * field is replaced with a call to the specified static method.
167      * This static method receives two parameters: the target object of
168      * the original
169      * write expression and the assigned value. The return type of the
170      * static method is <code>void</code>.
171      *
172      * <p>For example, the program below
173      *
174      * <ul><pre>Point p = new Point();
175      * p.x = 3;</pre></ul>
176      *
177      * <p>can be translated into:
178      *
179      * <ul><pre>Point p = new Point();
180      * Accessor.writeX(3);</pre></ul>
181      *
182      * <p>where
183      *
184      * <ul><pre>public class Accessor {
185      * public static void writeX(Object target, int value) { ... }
186      * }</pre></ul>
187      *
188      * <p>The type of the first parameter of <code>writeX()</code> must
189      * be <code>java.lang.Object</code> independently of the actual
190      * type of <code>target</code>. The type of the second parameter
191      * is the same as the field type.
192      *
193      * @param field the field.
194      * @param calledClass the class in which the static method is
195      * declared.
196      * @param calledMethod the name of the static method.
197      */

198     public void replaceFieldWrite(CtField field,
199                                   CtClass calledClass, String JavaDoc calledMethod) {
200         transformers = new TransformWriteField(transformers, field,
201                                                calledClass.getName(),
202                                                calledMethod);
203     }
204
205     /**
206      * Modify method invocations in a method body so that a different
207      * method is invoked.
208      *
209      * <p>Note that the target object, the parameters, or
210      * the type of invocation
211      * (static method call, interface call, or private method call)
212      * are not modified. Only the method name is changed. The substituted
213      * method must have the same signature that the original one has.
214      * If the original method is a static method, the substituted method
215      * must be static.
216      *
217      * @param origMethod original method
218      * @param substMethod substituted method
219      */

220     public void redirectMethodCall(CtMethod origMethod,
221                                    CtMethod substMethod)
222         throws CannotCompileException
223     {
224         String JavaDoc d1 = origMethod.getMethodInfo2().getDescriptor();
225         String JavaDoc d2 = substMethod.getMethodInfo2().getDescriptor();
226         if (!d1.equals(d2))
227             throw new CannotCompileException("signature mismatch");
228
229         transformers = new TransformCall(transformers, origMethod,
230                                          substMethod);
231     }
232
233     /**
234      * Insert a call to another method before an existing method call.
235      * That "before" method must be static. The return type must be
236      * <code>void</code>. As parameters, the before method receives
237      * the target object and all the parameters to the originally invoked
238      * method. For example, if the originally invoked method is
239      * <code>move()</code>:
240      *
241      * <ul><pre>class Point {
242      * Point move(int x, int y) { ... }
243      * }</pre></ul>
244      *
245      * <p>Then the before method must be something like this:
246      *
247      * <ul><pre>class Verbose {
248      * static void print(Point target, int x, int y) { ... }
249      * }</pre></ul>
250      *
251      * <p>The <code>CodeConverter</code> would translate bytecode
252      * equivalent to:
253      *
254      * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
255      *
256      * <p>into the bytecode equivalent to:
257      *
258      * <ul><pre>int tmp1 = x + y;
259      * int tmp2 = 0;
260      * Verbose.print(p, tmp1, tmp2);
261      * Point p2 = p.move(tmp1, tmp2);</pre></ul>
262      *
263      * @param origMethod the method originally invoked.
264      * @param beforeMethod the method invoked before
265      * <code>origMethod</code>.
266      */

267     public void insertBeforeMethod(CtMethod origMethod,
268                                    CtMethod beforeMethod)
269         throws CannotCompileException
270     {
271         try {
272             transformers = new TransformBefore(transformers, origMethod,
273                                                beforeMethod);
274         }
275         catch (NotFoundException e) {
276             throw new CannotCompileException(e);
277         }
278     }
279
280     /**
281      * Inserts a call to another method after an existing method call.
282      * That "after" method must be static. The return type must be
283      * <code>void</code>. As parameters, the after method receives
284      * the target object and all the parameters to the originally invoked
285      * method. For example, if the originally invoked method is
286      * <code>move()</code>:
287      *
288      * <ul><pre>class Point {
289      * Point move(int x, int y) { ... }
290      * }</pre></ul>
291      *
292      * <p>Then the after method must be something like this:
293      *
294      * <ul><pre>class Verbose {
295      * static void print(Point target, int x, int y) { ... }
296      * }</pre></ul>
297      *
298      * <p>The <code>CodeConverter</code> would translate bytecode
299      * equivalent to:
300      *
301      * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
302      *
303      * <p>into the bytecode equivalent to:
304      *
305      * <ul><pre>int tmp1 = x + y;
306      * int tmp2 = 0;
307      * Point p2 = p.move(tmp1, tmp2);
308      * Verbose.print(p, tmp1, tmp2);</pre></ul>
309      *
310      * @param origMethod the method originally invoked.
311      * @param afterMethod the method invoked after
312      * <code>origMethod</code>.
313      */

314     public void insertAfterMethod(CtMethod origMethod,
315                                   CtMethod afterMethod)
316         throws CannotCompileException
317     {
318         try {
319             transformers = new TransformAfter(transformers, origMethod,
320                                                afterMethod);
321         }
322         catch (NotFoundException e) {
323             throw new CannotCompileException(e);
324         }
325     }
326
327     /**
328      * Performs code conversion.
329      */

330     void doit(CtClass clazz, MethodInfo minfo, ConstPool cp)
331         throws CannotCompileException
332     {
333         Transformer t;
334
335         CodeAttribute codeAttr = minfo.getCodeAttribute();
336         if (codeAttr == null || transformers == null)
337             return;
338
339         for (t = transformers; t != null; t = t.getNext())
340             t.initialize(cp, codeAttr);
341
342         CodeIterator iterator = codeAttr.iterator();
343         while (iterator.hasNext()) {
344             try {
345                 int pos = iterator.next();
346                 for (t = transformers; t != null; t = t.getNext())
347                     pos = t.transform(clazz, pos, iterator, cp);
348             }
349             catch (BadBytecode e) {
350                 throw new CannotCompileException(e);
351             }
352         }
353
354         int locals = 0;
355         for (t = transformers; t != null; t = t.getNext()) {
356             int s = t.extraLocals();
357             if (s > locals)
358                 locals = s;
359         }
360
361         for (t = transformers; t != null; t = t.getNext())
362             t.clean();
363
364         codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);
365     }
366 }
367
Popular Tags