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