/Users/lyon/j4p/src/javassist/CodeConverter.java

1    /* 
2     * Javassist, a Java-bytecode translator toolkit. 
3     * Copyright (C) 1999-2003 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 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 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 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 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          String d1 = origMethod.getMethodInfo2().getDescriptor(); 
224          String d2 = substMethod.getMethodInfo2().getDescriptor(); 
225          if (!d1.equals(d2)) 
226              throw new CannotCompileException("signature mismatch"); 
227   
228          transformers = new TransformCall(transformers, origMethod, 
229                  substMethod); 
230      } 
231   
232      /** 
233       * Insert a call to another method before an existing method call. 
234       * That "before" method must be static.  The return type must be 
235       * <code>void</code>.  As parameters, the before method receives 
236       * the target object and all the parameters to the originally invoked 
237       * method.  For example, if the originally invoked method is 
238       * <code>move()</code>: 
239       * 
240       * <ul><pre>class Point { 
241       *     Point move(int x, int y) { ... } 
242       * }</pre></ul> 
243       * 
244       * <p>Then the before method must be something like this: 
245       * 
246       * <ul><pre>class Verbose { 
247       *     static void print(Point target, int x, int y) { ... } 
248       * }</pre></ul> 
249       * 
250       * <p>The <code>CodeConverter</code> would translate bytecode 
251       * equivalent to: 
252       * 
253       * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul> 
254       * 
255       * <p>into the bytecode equivalent to: 
256       * 
257       * <ul><pre>int tmp1 = x + y; 
258       * int tmp2 = 0; 
259       * Verbose.print(p, tmp1, tmp2); 
260       * Point p2 = p.move(tmp1, tmp2);</pre></ul> 
261       * 
262       * @param origMethod        the method originally invoked. 
263       * @param beforeMethod      the method invoked before 
264       *                          <code>origMethod</code>. 
265       */ 
266      public void insertBeforeMethod(CtMethod origMethod, 
267                                     CtMethod beforeMethod) 
268              throws CannotCompileException { 
269          try { 
270              transformers = new TransformBefore(transformers, origMethod, 
271                      beforeMethod); 
272          } catch (NotFoundException e) { 
273              throw new CannotCompileException(e); 
274          } 
275      } 
276   
277      /** 
278       * Inserts a call to another method after an existing method call. 
279       * That "after" method must be static.  The return type must be 
280       * <code>void</code>.  As parameters, the after method receives 
281       * the target object and all the parameters to the originally invoked 
282       * method.  For example, if the originally invoked method is 
283       * <code>move()</code>: 
284       * 
285       * <ul><pre>class Point { 
286       *     Point move(int x, int y) { ... } 
287       * }</pre></ul> 
288       * 
289       * <p>Then the after method must be something like this: 
290       * 
291       * <ul><pre>class Verbose { 
292       *     static void print(Point target, int x, int y) { ... } 
293       * }</pre></ul> 
294       * 
295       * <p>The <code>CodeConverter</code> would translate bytecode 
296       * equivalent to: 
297       * 
298       * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul> 
299       * 
300       * <p>into the bytecode equivalent to: 
301       * 
302       * <ul><pre>int tmp1 = x + y; 
303       * int tmp2 = 0; 
304       * Point p2 = p.move(tmp1, tmp2); 
305       * Verbose.print(p, tmp1, tmp2);</pre></ul> 
306       * 
307       * @param origMethod        the method originally invoked. 
308       * @param afterMethod       the method invoked after 
309       *                          <code>origMethod</code>. 
310       */ 
311      public void insertAfterMethod(CtMethod origMethod, 
312                                    CtMethod afterMethod) 
313              throws CannotCompileException { 
314          try { 
315              transformers = new TransformAfter(transformers, origMethod, 
316                      afterMethod); 
317          } catch (NotFoundException e) { 
318              throw new CannotCompileException(e); 
319          } 
320      } 
321   
322      /** 
323       * Performs code conversion. 
324       */ 
325      void doit(CtClass clazz, MethodInfo minfo, ConstPool cp) 
326              throws CannotCompileException { 
327          Transformer t; 
328   
329          CodeAttribute codeAttr = minfo.getCodeAttribute(); 
330          if (codeAttr == null || transformers == null) 
331              return; 
332   
333          for (t = transformers; t != null; t = t.getNext()) 
334              t.initialize(cp, codeAttr); 
335   
336          CodeIterator iterator = codeAttr.iterator(); 
337          while (iterator.hasNext()) { 
338              try { 
339                  int pos = iterator.next(); 
340                  for (t = transformers; t != null; t = t.getNext()) 
341                      pos = t.transform(clazz, pos, iterator, cp); 
342              } catch (BadBytecode e) { 
343                  throw new CannotCompileException(e); 
344              } 
345          } 
346   
347          int locals = 0; 
348          for (t = transformers; t != null; t = t.getNext()) { 
349              int s = t.extraLocals(); 
350              if (s > locals) 
351                  locals = s; 
352          } 
353   
354          for (t = transformers; t != null; t = t.getNext()) 
355              t.clean(); 
356   
357          codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals); 
358      } 
359  } 
360