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.*;
19
20/**
21 * An instance of <code>CtMethod</code> represents a method.
22 *
23 * <p>See the super class <code>CtBehavior</code> since
24 * a number of useful methods are in <code>CtBehavior</code>.
25 * A number of useful factory methods are in <code>CtNewMethod</code>.
26 *
27 * @see CtClass#getDeclaredMethods()
28 * @see CtNewMethod
29 */
30public final class CtMethod extends CtBehavior {
31    protected String cachedStringRep;
32
33    /**
34     * @see #make(MethodInfo minfo, CtClass declaring)
35     */
36    CtMethod(MethodInfo minfo, CtClass declaring) {
37        super(declaring, minfo);
38        cachedStringRep = null;
39    }
40
41    /**
42     * Creates a public abstract method.  The created method must be
43     * added to a class with <code>CtClass.addMethod()</code>.
44     *
45     * @param declaring         the class to which the created method is added.
46     * @param returnType        the type of the returned value
47     * @param mname             the method name
48     * @param parameters        a list of the parameter types
49     *
50     * @see CtClass#addMethod(CtMethod)
51     */
52    public CtMethod(CtClass returnType, String mname,
53                    CtClass[] parameters, CtClass declaring) {
54        this(null, declaring);
55        ConstPool cp = declaring.getClassFile2().getConstPool();
56        String desc = Descriptor.ofMethod(returnType, parameters);
57        methodInfo = new MethodInfo(cp, mname, desc);
58        setModifiers(Modifier.PUBLIC | Modifier.ABSTRACT);
59    }
60
61    /**
62     * Creates a copy of a <code>CtMethod</code> object.
63     * The created method must be
64     * added to a class with <code>CtClass.addMethod()</code>.
65     *
66     * <p>All occurrences of class names in the created method
67     * are replaced with names specified by
68     * <code>map</code> if <code>map</code> is not <code>null</code>.
69     *
70     * <p>For example, suppose that a method <code>at()</code> is as
71     * follows:
72     *
73     * <ul><pre>public X at(int i) {
74     *     return (X)super.elementAt(i);
75     * }</pre></ul>
76     *
77     * <p>(<code>X</code> is a class name.)  If <code>map</code> substitutes
78     * <code>String</code> for <code>X</code>, then the created method is:
79     *
80     * <ul><pre>public String at(int i) {
81     *     return (String)super.elementAt(i);
82     * }</pre></ul>
83     *
84     * <p>By default, all the occurrences of the names of the class
85     * declaring <code>at()</code> and the superclass are replaced
86     * with the name of the class and the superclass that the
87     * created method is added to.
88     * This is done whichever <code>map</code> is null or not.
89     * To prevent this replacement, call <code>ClassMap.fix()</code>
90     * or <code>put()</code> to explicitly specify replacement.
91     *
92     * <p><b>Note:</b> if the <code>.class</code> notation (for example,
93     * <code>String.class</code>) is included in an expression, the
94     * Javac compiler may produce a helper method.
95     * Since this constructor never
96     * copies this helper method, the programmers have the responsiblity of
97     * copying it.  Otherwise, use <code>Class.forName()</code> in the
98     * expression.
99     *
100     * @param src       the source method.
101     * @param declaring    the class to which the created method is added.
102     * @param map       the hashtable associating original class names
103     *                  with substituted names.
104     *                  It can be <code>null</code>.
105     *
106     * @see CtClass#addMethod(CtMethod)
107     * @see ClassMap#fix(String)
108     */
109    public CtMethod(CtMethod src, CtClass declaring, ClassMap map)
110        throws CannotCompileException
111    {
112        this(null, declaring);
113        copy(src, false, map);
114    }
115
116    /**
117     * Compiles the given source code and creates a method.
118     * This method simply delegates to <code>make()</code> in
119     * <code>CtNewMethod</code>.  See it for more details.
120     * <code>CtNewMethod</code> has a number of useful factory methods.
121     *
122     * @param src               the source text.
123     * @param declaring    the class to which the created method is added.
124     * @see CtNewMethod#make(String, CtClass)
125     */
126    public static CtMethod make(String src, CtClass declaring)
127        throws CannotCompileException
128    {
129        return CtNewMethod.make(src, declaring);
130    }
131
132    /**
133     * Creates a method from a <code>MethodInfo</code> object.
134     *
135     * @param declaring     the class declaring the method.
136     * @throws CannotCompileException       if the the <code>MethodInfo</code>
137     *          object and the declaring class have different
138     *          <code>ConstPool</code> objects
139     * @since 3.6
140     */
141    public static CtMethod make(MethodInfo minfo, CtClass declaring)
142        throws CannotCompileException
143    {
144        if (declaring.getClassFile2().getConstPool() != minfo.getConstPool())
145            throw new CannotCompileException("bad declaring class");
146
147        return new CtMethod(minfo, declaring);
148    }
149
150    /**
151     * Returns a hash code value for the method.
152     * If two methods have the same name and signature, then
153     * the hash codes for the two methods are equal.
154     */
155    public int hashCode() {
156        return getStringRep().hashCode();
157    }
158
159    /**
160     * This method is invoked when setName() or replaceClassName()
161     * in CtClass is called.
162     */
163    void nameReplaced() {
164        cachedStringRep = null;
165    }
166
167    /* This method is also called by CtClassType.getMethods0().
168     */
169    final String getStringRep() {
170        if (cachedStringRep == null)
171            cachedStringRep = methodInfo.getName()
172                + Descriptor.getParamDescriptor(methodInfo.getDescriptor());
173
174        return cachedStringRep;
175    }
176
177    /**
178     * Indicates whether <code>obj</code> has the same name and the
179     * same signature as this method.
180     */
181    public boolean equals(Object obj) {
182        return obj != null && obj instanceof CtMethod
183               && ((CtMethod)obj).getStringRep().equals(getStringRep());
184    }
185
186    /**
187     * Returns the method name followed by parameter types
188     * such as <code>javassist.CtMethod.setBody(String)</code>.
189     *
190     * @since 3.5
191     */
192    public String getLongName() {
193        return getDeclaringClass().getName() + "."
194               + getName() + Descriptor.toString(getSignature());
195    }
196
197    /**
198     * Obtains the name of this method.
199     */
200    public String getName() {
201        return methodInfo.getName();
202    }
203
204    /**
205     * Changes the name of this method.
206     */
207    public void setName(String newname) {
208        declaringClass.checkModify();
209        methodInfo.setName(newname);
210    }
211
212    /**
213     * Obtains the type of the returned value.
214     */
215    public CtClass getReturnType() throws NotFoundException {
216        return getReturnType0();
217    }
218
219    /**
220     * Returns true if the method body is empty, that is, <code>{}</code>.
221     * It also returns true if the method is an abstract method.
222     */
223    public boolean isEmpty() {
224        CodeAttribute ca = getMethodInfo2().getCodeAttribute();
225        if (ca == null)         // abstract or native
226            return (getModifiers() & Modifier.ABSTRACT) != 0;
227
228        CodeIterator it = ca.iterator();
229        try {
230            return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN
231                && !it.hasNext();
232        }
233        catch (BadBytecode e) {}
234        return false;
235    }
236
237    /**
238     * Copies a method body from another method.
239     * If this method is abstract, the abstract modifier is removed
240     * after the method body is copied.
241     *
242     * <p>All occurrences of the class names in the copied method body
243     * are replaced with the names specified by
244     * <code>map</code> if <code>map</code> is not <code>null</code>.
245     *
246     * @param src       the method that the body is copied from.
247     * @param map       the hashtable associating original class names
248     *                  with substituted names.
249     *                  It can be <code>null</code>.
250     */
251    public void setBody(CtMethod src, ClassMap map)
252        throws CannotCompileException
253    {
254        setBody0(src.declaringClass, src.methodInfo,
255                 declaringClass, methodInfo, map);
256    }
257
258    /**
259     * Replace a method body with a new method body wrapping the
260     * given method.
261     *
262     * @param mbody             the wrapped method
263     * @param constParam        the constant parameter given to
264     *                          the wrapped method
265     *                          (maybe <code>null</code>).
266     *
267     * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
268     */
269    public void setWrappedBody(CtMethod mbody, ConstParameter constParam)
270        throws CannotCompileException
271    {
272        declaringClass.checkModify();
273
274        CtClass clazz = getDeclaringClass();
275        CtClass[] params;
276        CtClass retType;
277        try {
278            params = getParameterTypes();
279            retType = getReturnType();
280        }
281        catch (NotFoundException e) {
282            throw new CannotCompileException(e);
283        }
284
285        Bytecode code = CtNewWrappedMethod.makeBody(clazz,
286                                                    clazz.getClassFile2(),
287                                                    mbody,
288                                                    params, retType,
289                                                    constParam);
290        CodeAttribute cattr = code.toCodeAttribute();
291        methodInfo.setCodeAttribute(cattr);
292        methodInfo.setAccessFlags(methodInfo.getAccessFlags()
293                                  & ~AccessFlag.ABSTRACT);
294        // rebuilding a stack map table is not needed.
295    }
296
297    // inner classes
298
299    /**
300     * Instances of this class represent a constant parameter.
301     * They are used to specify the parameter given to the methods
302     * created by <code>CtNewMethod.wrapped()</code>.
303     *
304     * @see CtMethod#setWrappedBody(CtMethod,CtMethod.ConstParameter)
305     * @see CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
306     * @see CtNewConstructor#make(CtClass[],CtClass[],int,CtMethod,CtMethod.ConstParameter,CtClass)
307     */
308    public static class ConstParameter {
309        /**
310         * Makes an integer constant.
311         *
312         * @param i             the constant value.
313         */
314        public static ConstParameter integer(int i) {
315            return new IntConstParameter(i);
316        }
317
318        /**
319         * Makes a long integer constant.
320         *
321         * @param i             the constant value.
322         */
323        public static ConstParameter integer(long i) {
324            return new LongConstParameter(i);
325        }
326
327        /**
328         * Makes an <code>String</code> constant.
329         *
330         * @param s             the constant value.
331         */
332        public static ConstParameter string(String s) {
333            return new StringConstParameter(s);
334        }
335
336        ConstParameter() {}
337
338        /**
339         * @return      the size of the stack consumption.
340         */
341        int compile(Bytecode code) throws CannotCompileException {
342            return 0;
343        }
344
345        String descriptor() {
346            return defaultDescriptor();
347        }
348
349        /**
350         * @see CtNewWrappedMethod
351         */
352        static String defaultDescriptor() {
353            return "([Ljava/lang/Object;)Ljava/lang/Object;";
354        }
355
356        /**
357         * Returns the descriptor for constructors.
358         *
359         * @see CtNewWrappedConstructor
360         */
361        String constDescriptor() {
362            return defaultConstDescriptor();
363        }
364
365        /**
366         * Returns the default descriptor for constructors.
367         */
368        static String defaultConstDescriptor() {
369            return "([Ljava/lang/Object;)V";
370        }
371    }
372
373    static class IntConstParameter extends ConstParameter {
374        int param;
375
376        IntConstParameter(int i) {
377            param = i;
378        }
379
380        int compile(Bytecode code) throws CannotCompileException {
381            code.addIconst(param);
382            return 1;
383        }
384
385        String descriptor() {
386            return "([Ljava/lang/Object;I)Ljava/lang/Object;";
387        }
388
389        String constDescriptor() {
390            return "([Ljava/lang/Object;I)V";
391        }
392    }
393
394    static class LongConstParameter extends ConstParameter {
395        long param;
396
397        LongConstParameter(long l) {
398            param = l;
399        }
400
401        int compile(Bytecode code) throws CannotCompileException {
402            code.addLconst(param);
403            return 2;
404        }
405
406        String descriptor() {
407            return "([Ljava/lang/Object;J)Ljava/lang/Object;";
408        }
409
410        String constDescriptor() {
411            return "([Ljava/lang/Object;J)V";
412        }
413    }
414
415    static class StringConstParameter extends ConstParameter {
416        String param;
417
418        StringConstParameter(String s) {
419            param = s;
420        }
421
422        int compile(Bytecode code) throws CannotCompileException {
423            code.addLdc(param);
424            return 1;
425        }
426
427        String descriptor() {
428            return "([Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
429        }
430
431        String constDescriptor() {
432            return "([Ljava/lang/Object;Ljava/lang/String;)V";
433        }
434    }
435}
436