Home | History | Annotate | Download | only in javassist
      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 
     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  */
     30 public 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