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
16package javassist;
17
18import javassist.bytecode.*;
19import javassist.compiler.Javac;
20import javassist.compiler.CompileError;
21import 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 */
29public 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    {
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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