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