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