/Users/lyon/j4p/src/javassist/CtNewMethod.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.compiler.Javac; 
20   import javassist.compiler.CompileError; 
21   import javassist.CtMethod.ConstParameter; 
22    
23   /** 
24    * A collection of static methods for creating a <code>CtMethod</code>. 
25    * An instance of this class does not make any sense. 
26    * 
27    * @see CtClass#addMethod(CtMethod) 
28    */ 
29   public class CtNewMethod { 
30    
31       /** 
32        * Compiles the given source code and creates a method. 
33        * The source code must include not only the method body 
34        * but the whole declaration, for example, 
35        * 
36        * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul> 
37        * 
38        * @param src               the source text. 
39        * @param declaring    the class to which the created method is added. 
40        */ 
41       public static CtMethod make(String src, CtClass declaring) 
42               throws CannotCompileException { 
43           return make(src, declaring, null, null); 
44       } 
45    
46       /** 
47        * Compiles the given source code and creates a method. 
48        * The source code must include not only the method body 
49        * but the whole declaration, for example, 
50        * 
51        * <ul><pre>"public Object id(Object obj) { return obj; }"</pre></ul> 
52        * 
53        * <p>If the source code includes <code>$proceed()</code>, then 
54        * it is compiled into a method call on the specified object. 
55        * 
56        * @param src               the source text. 
57        * @param declaring    the class to which the created method is added. 
58        * @param delegateObj       the source text specifying the object 
59        *                          that is called on by <code>$proceed()</code>. 
60        * @param delegateMethod    the name of the method 
61        *                          that is called by <code>$proceed()</code>. 
62        */ 
63       public static CtMethod make(String src, CtClass declaring, 
64                                   String delegateObj, String delegateMethod) 
65               throws CannotCompileException { 
66           Javac compiler = new Javac(declaring); 
67           try { 
68               if (delegateMethod != null) 
69                   compiler.recordProceed(delegateObj, delegateMethod); 
70    
71               CtMember obj = compiler.compile(src); 
72               if (obj instanceof CtMethod) 
73                   return (CtMethod) obj; 
74           } catch (CompileError e) { 
75               throw new CannotCompileException(e); 
76           } 
77    
78           throw new CannotCompileException("not a method"); 
79       } 
80    
81       /** 
82        * Creates a public method. 
83        * 
84        * @param returnType        the type of the returned value. 
85        * @param mname             the method name. 
86        * @param parameters        a list of the parameter types. 
87        * @param exceptions        a list of the exception types. 
88        * @param body              the source text of the method body. 
89        *                  It must be a block surrounded by <code>{}</code>. 
90        *                  If it is <code>null</code>, the created method 
91        *                  does nothing except returning zero or null. 
92        * @param declaring    the class to which the created method is added. 
93        */ 
94       public static CtMethod make(CtClass returnType, String mname, 
95                                   CtClass[] parameters, CtClass[] exceptions, 
96                                   String body, CtClass declaring) 
97               throws CannotCompileException { 
98           try { 
99               CtMethod cm 
100                      = new CtMethod(returnType, mname, parameters, declaring); 
101              cm.setExceptionTypes(exceptions); 
102              cm.setBody(body); 
103              return cm; 
104          } catch (NotFoundException e) { 
105              throw new CannotCompileException(e); 
106          } 
107      } 
108   
109      /** 
110       * Creates a copy of a method.  This method is provided for creating 
111       * a new method based on an existing method. 
112       * 
113       * @param src       the source method. 
114       * @param declaring    the class to which the created method is added. 
115       * @param map       the hashtable associating original class names 
116       *                  with substituted names. 
117       *                  It can be <code>null</code>. 
118       * 
119       * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) 
120       */ 
121      public static CtMethod copy(CtMethod src, CtClass declaring, 
122                                  ClassMap map) throws CannotCompileException { 
123          return new CtMethod(src, declaring, map); 
124      } 
125   
126      /** 
127       * Creates a copy of a method with a new name. 
128       * This method is provided for creating 
129       * a new method based on an existing method. 
130       * 
131       * @param src       the source method. 
132       * @param name      the name of the created method. 
133       * @param declaring    the class to which the created method is added. 
134       * @param map       the hashtable associating original class names 
135       *                  with substituted names. 
136       *                  It can be <code>null</code>. 
137       * 
138       * @see CtMethod#CtMethod(CtMethod,CtClass,ClassMap) 
139       */ 
140      public static CtMethod copy(CtMethod src, String name, CtClass declaring, 
141                                  ClassMap map) throws CannotCompileException { 
142          CtMethod cm = new CtMethod(src, declaring, map); 
143          cm.setName(name); 
144          return cm; 
145      } 
146   
147      /** 
148       * Creates a public abstract method. 
149       * 
150       * @param returnType        the type of the returned value 
151       * @param mname             the method name 
152       * @param parameters        a list of the parameter types 
153       * @param exceptions        a list of the exception types 
154       * @param declaring    the class to which the created method is added. 
155       * 
156       * @see CtMethod#CtMethod(CtClass,String,CtClass[],CtClass) 
157       */ 
158      public static CtMethod abstractMethod(CtClass returnType, 
159                                            String mname, 
160                                            CtClass[] parameters, 
161                                            CtClass[] exceptions, 
162                                            CtClass declaring) 
163              throws NotFoundException { 
164          CtMethod cm = new CtMethod(returnType, mname, parameters, declaring); 
165          cm.setExceptionTypes(exceptions); 
166          return cm; 
167      } 
168   
169      /** 
170       * Creates a public getter method.  The getter method returns the value 
171       * of the specified field in the class to which this method is added. 
172       * The created method is initially not static even if the field is 
173       * static.  Change the modifiers if the method should be static. 
174       * 
175       * @param methodName        the name of the getter 
176       * @param field             the field accessed. 
177       */ 
178      public static CtMethod getter(String methodName, CtField field) 
179              throws CannotCompileException { 
180          FieldInfo finfo = field.getFieldInfo2(); 
181          String fieldType = finfo.getDescriptor(); 
182          String desc = "()" + fieldType; 
183          ConstPool cp = finfo.getConstPool(); 
184          MethodInfo minfo = new MethodInfo(cp, methodName, desc); 
185          minfo.setAccessFlags(AccessFlag.PUBLIC); 
186   
187          Bytecode code = new Bytecode(cp, 2, 1); 
188          try { 
189              String fieldName = finfo.getName(); 
190              if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { 
191                  code.addAload(0); 
192                  code.addGetfield(Bytecode.THIS, fieldName, fieldType); 
193              } else 
194                  code.addGetstatic(Bytecode.THIS, fieldName, fieldType); 
195   
196              code.addReturn(field.getType()); 
197          } catch (NotFoundException e) { 
198              throw new CannotCompileException(e); 
199          } 
200   
201          minfo.setCodeAttribute(code.toCodeAttribute()); 
202          return new CtMethod(minfo, field.getDeclaringClass()); 
203      } 
204   
205      /** 
206       * Creates a public setter method.  The setter method assigns the 
207       * value of the first parameter to the specified field 
208       * in the class to which this method is added. 
209       * The created method is initially not static even if the field is 
210       * static.  Change the modifiers if the method should be static. 
211       * 
212       * @param methodName        the name of the setter 
213       * @param field             the field accessed. 
214       */ 
215      public static CtMethod setter(String methodName, CtField field) 
216              throws CannotCompileException { 
217          FieldInfo finfo = field.getFieldInfo2(); 
218          String fieldType = finfo.getDescriptor(); 
219          String desc = "(" + fieldType + ")V"; 
220          ConstPool cp = finfo.getConstPool(); 
221          MethodInfo minfo = new MethodInfo(cp, methodName, desc); 
222          minfo.setAccessFlags(AccessFlag.PUBLIC); 
223   
224          Bytecode code = new Bytecode(cp, 3, 3); 
225          try { 
226              String fieldName = finfo.getName(); 
227              if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) { 
228                  code.addAload(0); 
229                  code.addLoad(1, field.getType()); 
230                  code.addPutfield(Bytecode.THIS, fieldName, fieldType); 
231              } else { 
232                  code.addLoad(0, field.getType()); 
233                  code.addPutstatic(Bytecode.THIS, fieldName, fieldType); 
234              } 
235   
236              code.addReturn(null); 
237          } catch (NotFoundException e) { 
238              throw new CannotCompileException(e); 
239          } 
240   
241          minfo.setCodeAttribute(code.toCodeAttribute()); 
242          return new CtMethod(minfo, field.getDeclaringClass()); 
243      } 
244   
245      /** 
246       * Creates a method forwarding to a delegate in 
247       * a super class.  The created method calls a method specified 
248       * by <code>delegate</code> with all the parameters passed to the 
249       * created method.  If the delegate method returns a value, 
250       * the created method returns that value to the caller. 
251       * The delegate method must be declared in a super class. 
252       * 
253       * <p>The following method is an example of the created method. 
254       * 
255       * <ul><pre>int f(int p, int q) { 
256       *     return super.f(p, q); 
257       * }</pre></ul> 
258       * 
259       * <p>The name of the created method can be changed by 
260       * <code>setName()</code>. 
261       * 
262       * @param delegate    the method that the created method forwards to. 
263       * @param declaring         the class to which the created method is 
264       *                          added. 
265       */ 
266      public static CtMethod delegator(CtMethod delegate, CtClass declaring) 
267              throws CannotCompileException { 
268          try { 
269              return delegator0(delegate, declaring); 
270          } catch (NotFoundException e) { 
271              throw new CannotCompileException(e); 
272          } 
273      } 
274   
275      private static CtMethod delegator0(CtMethod delegate, CtClass declaring) 
276              throws CannotCompileException, NotFoundException { 
277          MethodInfo deleInfo = delegate.getMethodInfo2(); 
278          String methodName = deleInfo.getName(); 
279          String desc = deleInfo.getDescriptor(); 
280          ConstPool cp = declaring.getClassFile2().getConstPool(); 
281          MethodInfo minfo = new MethodInfo(cp, methodName, desc); 
282          minfo.setAccessFlags(deleInfo.getAccessFlags()); 
283   
284          ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute(); 
285          if (eattr != null) 
286              minfo.setExceptionsAttribute( 
287                      (ExceptionsAttribute) eattr.copy(cp, null)); 
288   
289          Bytecode code = new Bytecode(cp, 0, 0); 
290          boolean isStatic = Modifier.isStatic(delegate.getModifiers()); 
291          CtClass deleClass = delegate.getDeclaringClass(); 
292          CtClass[] params = delegate.getParameterTypes(); 
293          int s; 
294          if (isStatic) { 
295              s = code.addLoadParameters(params, 0); 
296              code.addInvokestatic(deleClass, methodName, desc); 
297          } else { 
298              code.addLoad(0, deleClass); 
299              s = code.addLoadParameters(params, 1); 
300              code.addInvokespecial(deleClass, methodName, desc); 
301          } 
302   
303          code.addReturn(delegate.getReturnType()); 
304          code.setMaxLocals(++s); 
305          code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value 
306          minfo.setCodeAttribute(code.toCodeAttribute()); 
307          return new CtMethod(minfo, declaring); 
308      } 
309   
310      /** 
311       * Creates a wrapped method.  The wrapped method receives parameters 
312       * in the form of an array of <code>Object</code>. 
313       * 
314       * <p>The body of the created method is a copy of the body of a method 
315       * specified by <code>body</code>.  However, it is wrapped in 
316       * parameter-conversion code. 
317       * 
318       * <p>The method specified by <code>body</code> must have this singature: 
319       * 
320       * <ul><code>Object method(Object[] params, &lt;type&gt; cvalue) 
321       * </code></ul> 
322       * 
323       * <p>The type of the <code>cvalue</code> depends on 
324       * <code>constParam</code>. 
325       * If <code>constParam</code> is <code>null</code>, the signature 
326       * must be: 
327       * 
328       * <ul><code>Object method(Object[] params)</code></ul> 
329       * 
330       * <p>The method body copied from <code>body</code> is wrapped in 
331       * parameter-conversion code, which converts parameters specified by 
332       * <code>parameterTypes</code> into an array of <code>Object</code>. 
333       * The returned value is also converted from the <code>Object</code> 
334       * type to the type specified by <code>returnType</code>.  Thus, 
335       * the resulting method body is as follows: 
336       * 
337       * <ul><pre>Object[] params = new Object[] { p0, p1, ... }; 
338       * &lt;<i>type</i>&gt; cvalue = &lt;<i>constant-value</i>&gt;; 
339       *  <i>... copied method body ...</i> 
340       * Object result = &lt;<i>returned value</i>&gt; 
341       * return (<i>&lt;returnType&gt;</i>)result; 
342       * </pre></ul> 
343       * 
344       * <p>The variables <code>p0</code>, <code>p2</code>, ... represent 
345       * formal parameters of the created method. 
346       * The value of <code>cvalue</code> is specified by 
347       * <code>constParam</code>. 
348       * 
349       * <p>If the type of a parameter or a returned value is a primitive 
350       * type, then the value is converted into a wrapper object such as 
351       * <code>java.lang.Integer</code>.  If the type of the returned value 
352       * is <code>void</code>, the returned value is discarded. 
353       * 
354       * <p><i>Example:</i> 
355       * 
356       * <ul><pre>ClassPool pool = ... ; 
357       * CtClass vec = pool.makeClass("intVector"); 
358       * vec.setSuperclass(pool.get("java.util.Vector")); 
359       * CtMethod addMethod = pool.getMethod("Sample", "add0"); 
360       * 
361       * CtClass[] argTypes = { CtClass.intType }; 
362       * CtMethod m = CtNewMethod.wrapped(CtClass.voidType, "add", argTypes, 
363       *                                  null, addMethod, null, vec); 
364       * vec.addMethod(m);</pre></ul> 
365       * 
366       * <p>where the class <code>Sample</code> is as follows: 
367       * 
368       * <ul><pre>public class Sample extends java.util.Vector { 
369       *     public Object add0(Object[] args) { 
370       *         super.addElement(args[0]); 
371       *         return null; 
372       *     } 
373       * }</pre></ul> 
374       * 
375       * <p>This program produces a class <code>intVector</code>: 
376       * 
377       * <ul><pre>public class intVector extends java.util.Vector { 
378       *     public void add(int p0) { 
379       *         Object[] args = new Object[] { p0 }; 
380       *         // begin of copied body 
381       *         super.addElement(args[0]); 
382       *         Object result = null; 
383       *         // end 
384       *     } 
385       * }</pre></ul> 
386       * 
387       * <p>Note that the type of the parameter to <code>add()</code> depends 
388       * only on the value of <code>argTypes</code> passed to 
389       * <code>CtNewMethod.wrapped()</code>.  Thus, it is easy to 
390       * modify this program to produce a 
391       * <code>StringVector</code> class, which is a vector containing 
392       * only <code>String</code> objects, and other vector classes. 
393       * 
394       * @param returnType        the type of the returned value. 
395       * @param mname             the method name. 
396       * @param parameters        a list of the parameter types. 
397       * @param exceptions        a list of the exception types. 
398       * @param body              the method body 
399       *                          (must not be a static method). 
400       * @param constParam        the constant parameter 
401       *                          (maybe <code>null</code>). 
402       * @param declaring         the class to which the created method is 
403       *                          added. 
404       */ 
405      public static CtMethod wrapped(CtClass returnType, 
406                                     String mname, 
407                                     CtClass[] parameterTypes, 
408                                     CtClass[] exceptionTypes, 
409                                     CtMethod body, ConstParameter constParam, 
410                                     CtClass declaring) 
411              throws CannotCompileException { 
412          return CtNewWrappedMethod.wrapped(returnType, mname, parameterTypes, 
413                  exceptionTypes, body, constParam, declaring); 
414      } 
415  } 
416