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 import javassist.compiler.Javac;
     20 import javassist.compiler.CompileError;
     21 import javassist.expr.ExprEditor;
     22 
     23 /**
     24  * <code>CtBehavior</code> represents a method, a constructor,
     25  * or a static constructor (class initializer).
     26  * It is the abstract super class of
     27  * <code>CtMethod</code> and <code>CtConstructor</code>.
     28  */
     29 public abstract class CtBehavior extends CtMember {
     30     protected MethodInfo methodInfo;
     31 
     32     protected CtBehavior(CtClass clazz, MethodInfo minfo) {
     33         super(clazz);
     34         methodInfo = minfo;
     35     }
     36 
     37     /**
     38      * @param isCons        true if this is a constructor.
     39      */
     40     void copy(CtBehavior src, boolean isCons, ClassMap map)
     41         throws CannotCompileException
     42     {
     43         CtClass declaring = declaringClass;
     44         MethodInfo srcInfo = src.methodInfo;
     45         CtClass srcClass = src.getDeclaringClass();
     46         ConstPool cp = declaring.getClassFile2().getConstPool();
     47 
     48         map = new ClassMap(map);
     49         map.put(srcClass.getName(), declaring.getName());
     50         try {
     51             boolean patch = false;
     52             CtClass srcSuper = srcClass.getSuperclass();
     53             CtClass destSuper = declaring.getSuperclass();
     54             String destSuperName = null;
     55             if (srcSuper != null && destSuper != null) {
     56                 String srcSuperName = srcSuper.getName();
     57                 destSuperName = destSuper.getName();
     58                 if (!srcSuperName.equals(destSuperName))
     59                     if (srcSuperName.equals(CtClass.javaLangObject))
     60                         patch = true;
     61                     else
     62                         map.putIfNone(srcSuperName, destSuperName);
     63             }
     64 
     65             // a stack map table is copied from srcInfo.
     66             methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map);
     67             if (isCons && patch)
     68                 methodInfo.setSuperclass(destSuperName);
     69         }
     70         catch (NotFoundException e) {
     71             throw new CannotCompileException(e);
     72         }
     73         catch (BadBytecode e) {
     74             throw new CannotCompileException(e);
     75         }
     76     }
     77 
     78     protected void extendToString(StringBuffer buffer) {
     79         buffer.append(' ');
     80         buffer.append(getName());
     81         buffer.append(' ');
     82         buffer.append(methodInfo.getDescriptor());
     83     }
     84 
     85     /**
     86      * Returns the method or constructor name followed by parameter types
     87      * such as <code>javassist.CtBehavior.stBody(String)</code>.
     88      *
     89      * @since 3.5
     90      */
     91     public abstract String getLongName();
     92 
     93     /**
     94      * Returns the MethodInfo representing this method/constructor in the
     95      * class file.
     96      */
     97     public MethodInfo getMethodInfo() {
     98         declaringClass.checkModify();
     99         return methodInfo;
    100     }
    101 
    102     /**
    103      * Returns the MethodInfo representing the method/constructor in the
    104      * class file (read only).
    105      * Normal applications do not need calling this method.  Use
    106      * <code>getMethodInfo()</code>.
    107      *
    108      * <p>The <code>MethodInfo</code> object obtained by this method
    109      * is read only.  Changes to this object might not be reflected
    110      * on a class file generated by <code>toBytecode()</code>,
    111      * <code>toClass()</code>, etc in <code>CtClass</code>.
    112      *
    113      * <p>This method is available even if the <code>CtClass</code>
    114      * containing this method is frozen.  However, if the class is
    115      * frozen, the <code>MethodInfo</code> might be also pruned.
    116      *
    117      * @see #getMethodInfo()
    118      * @see CtClass#isFrozen()
    119      * @see CtClass#prune()
    120      */
    121     public MethodInfo getMethodInfo2() { return methodInfo; }
    122 
    123     /**
    124      * Obtains the modifiers of the method/constructor.
    125      *
    126      * @return          modifiers encoded with
    127      *                  <code>javassist.Modifier</code>.
    128      * @see Modifier
    129      */
    130     public int getModifiers() {
    131         return AccessFlag.toModifier(methodInfo.getAccessFlags());
    132     }
    133 
    134     /**
    135      * Sets the encoded modifiers of the method/constructor.
    136      *
    137      * <p>Changing the modifiers may cause a problem.
    138      * For example, if a non-static method is changed to static,
    139      * the method will be rejected by the bytecode verifier.
    140      *
    141      * @see Modifier
    142      */
    143     public void setModifiers(int mod) {
    144         declaringClass.checkModify();
    145         methodInfo.setAccessFlags(AccessFlag.of(mod));
    146     }
    147 
    148     /**
    149      * Returns true if the class has the specified annotation class.
    150      *
    151      * @param clz the annotation class.
    152      * @return <code>true</code> if the annotation is found,
    153      *         otherwise <code>false</code>.
    154      * @since 3.11
    155      */
    156     public boolean hasAnnotation(Class clz) {
    157        MethodInfo mi = getMethodInfo2();
    158        AnnotationsAttribute ainfo = (AnnotationsAttribute)
    159                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
    160        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    161                    mi.getAttribute(AnnotationsAttribute.visibleTag);
    162        return CtClassType.hasAnnotationType(clz,
    163                                             getDeclaringClass().getClassPool(),
    164                                             ainfo, ainfo2);
    165     }
    166 
    167     /**
    168      * Returns the annotation if the class has the specified annotation class.
    169      * For example, if an annotation <code>@Author</code> is associated
    170      * with this method/constructor, an <code>Author</code> object is returned.
    171      * The member values can be obtained by calling methods on
    172      * the <code>Author</code> object.
    173      *
    174      * @param clz the annotation class.
    175      * @return the annotation if found, otherwise <code>null</code>.
    176      * @since 3.11
    177      */
    178     public Object getAnnotation(Class clz) throws ClassNotFoundException {
    179        MethodInfo mi = getMethodInfo2();
    180        AnnotationsAttribute ainfo = (AnnotationsAttribute)
    181                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
    182        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    183                    mi.getAttribute(AnnotationsAttribute.visibleTag);
    184        return CtClassType.getAnnotationType(clz,
    185                                             getDeclaringClass().getClassPool(),
    186                                             ainfo, ainfo2);
    187     }
    188 
    189     /**
    190      * Returns the annotations associated with this method or constructor.
    191      *
    192      * @return an array of annotation-type objects.
    193      * @see #getAvailableAnnotations()
    194      * @since 3.1
    195      */
    196     public Object[] getAnnotations() throws ClassNotFoundException {
    197        return getAnnotations(false);
    198    }
    199 
    200     /**
    201      * Returns the annotations associated with this method or constructor.
    202      * If any annotations are not on the classpath, they are not included
    203      * in the returned array.
    204      *
    205      * @return an array of annotation-type objects.
    206      * @see #getAnnotations()
    207      * @since 3.3
    208      */
    209     public Object[] getAvailableAnnotations(){
    210        try{
    211            return getAnnotations(true);
    212        }
    213        catch (ClassNotFoundException e){
    214            throw new RuntimeException("Unexpected exception", e);
    215        }
    216     }
    217 
    218     private Object[] getAnnotations(boolean ignoreNotFound)
    219        throws ClassNotFoundException
    220     {
    221        MethodInfo mi = getMethodInfo2();
    222        AnnotationsAttribute ainfo = (AnnotationsAttribute)
    223                    mi.getAttribute(AnnotationsAttribute.invisibleTag);
    224        AnnotationsAttribute ainfo2 = (AnnotationsAttribute)
    225                    mi.getAttribute(AnnotationsAttribute.visibleTag);
    226        return CtClassType.toAnnotationType(ignoreNotFound,
    227                                            getDeclaringClass().getClassPool(),
    228                                            ainfo, ainfo2);
    229     }
    230 
    231     /**
    232      * Returns the parameter annotations associated with this method or constructor.
    233      *
    234      * @return an array of annotation-type objects.  The length of the returned array is
    235      * equal to the number of the formal parameters.  If each parameter has no
    236      * annotation, the elements of the returned array are empty arrays.
    237      *
    238      * @see #getAvailableParameterAnnotations()
    239      * @see #getAnnotations()
    240      * @since 3.1
    241      */
    242     public Object[][] getParameterAnnotations() throws ClassNotFoundException {
    243         return getParameterAnnotations(false);
    244     }
    245 
    246     /**
    247      * Returns the parameter annotations associated with this method or constructor.
    248      * If any annotations are not on the classpath, they are not included in the
    249      * returned array.
    250      *
    251      * @return an array of annotation-type objects.  The length of the returned array is
    252      * equal to the number of the formal parameters.  If each parameter has no
    253      * annotation, the elements of the returned array are empty arrays.
    254      *
    255      * @see #getParameterAnnotations()
    256      * @see #getAvailableAnnotations()
    257      * @since 3.3
    258      */
    259     public Object[][] getAvailableParameterAnnotations(){
    260         try {
    261             return getParameterAnnotations(true);
    262         }
    263         catch(ClassNotFoundException e) {
    264             throw new RuntimeException("Unexpected exception", e);
    265         }
    266     }
    267 
    268     Object[][] getParameterAnnotations(boolean ignoreNotFound)
    269         throws ClassNotFoundException
    270     {
    271         MethodInfo mi = getMethodInfo2();
    272         ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute)
    273                     mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag);
    274         ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute)
    275                     mi.getAttribute(ParameterAnnotationsAttribute.visibleTag);
    276         return CtClassType.toAnnotationType(ignoreNotFound,
    277                                             getDeclaringClass().getClassPool(),
    278                                             ainfo, ainfo2, mi);
    279     }
    280 
    281     /**
    282      * Obtains parameter types of this method/constructor.
    283      */
    284     public CtClass[] getParameterTypes() throws NotFoundException {
    285         return Descriptor.getParameterTypes(methodInfo.getDescriptor(),
    286                                             declaringClass.getClassPool());
    287     }
    288 
    289     /**
    290      * Obtains the type of the returned value.
    291      */
    292     CtClass getReturnType0() throws NotFoundException {
    293         return Descriptor.getReturnType(methodInfo.getDescriptor(),
    294                                         declaringClass.getClassPool());
    295     }
    296 
    297     /**
    298      * Returns the method signature (the parameter types
    299      * and the return type).
    300      * The method signature is represented by a character string
    301      * called method descriptor, which is defined in the JVM specification.
    302      * If two methods/constructors have
    303      * the same parameter types
    304      * and the return type, <code>getSignature()</code> returns the
    305      * same string (the return type of constructors is <code>void</code>).
    306      *
    307      * <p>Note that the returned string is not the type signature
    308      * contained in the <code>SignatureAttirbute</code>.  It is
    309      * a descriptor.  To obtain a type signature, call the following
    310      * methods:
    311      *
    312      * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag)
    313      * </pre></ul>
    314      *
    315      * @see javassist.bytecode.Descriptor
    316      * @see javassist.bytecode.SignatureAttribute
    317      */
    318     public String getSignature() {
    319         return methodInfo.getDescriptor();
    320     }
    321 
    322     /**
    323      * Obtains exceptions that this method/constructor may throw.
    324      *
    325      * @return a zero-length array if there is no throws clause.
    326      */
    327     public CtClass[] getExceptionTypes() throws NotFoundException {
    328         String[] exceptions;
    329         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
    330         if (ea == null)
    331             exceptions = null;
    332         else
    333             exceptions = ea.getExceptions();
    334 
    335         return declaringClass.getClassPool().get(exceptions);
    336     }
    337 
    338     /**
    339      * Sets exceptions that this method/constructor may throw.
    340      */
    341     public void setExceptionTypes(CtClass[] types) throws NotFoundException {
    342         declaringClass.checkModify();
    343         if (types == null || types.length == 0) {
    344             methodInfo.removeExceptionsAttribute();
    345             return;
    346         }
    347 
    348         String[] names = new String[types.length];
    349         for (int i = 0; i < types.length; ++i)
    350             names[i] = types[i].getName();
    351 
    352         ExceptionsAttribute ea = methodInfo.getExceptionsAttribute();
    353         if (ea == null) {
    354             ea = new ExceptionsAttribute(methodInfo.getConstPool());
    355             methodInfo.setExceptionsAttribute(ea);
    356         }
    357 
    358         ea.setExceptions(names);
    359     }
    360 
    361     /**
    362      * Returns true if the body is empty.
    363      */
    364     public abstract boolean isEmpty();
    365 
    366     /**
    367      * Sets a method/constructor body.
    368      *
    369      * @param src       the source code representing the body.
    370      *                  It must be a single statement or block.
    371      *                  If it is <code>null</code>, the substituted
    372      *                  body does nothing except returning zero or null.
    373      */
    374     public void setBody(String src) throws CannotCompileException {
    375         setBody(src, null, null);
    376     }
    377 
    378     /**
    379      * Sets a method/constructor body.
    380      *
    381      * @param src       the source code representing the body.
    382      *                  It must be a single statement or block.
    383      *                  If it is <code>null</code>, the substituted
    384      *                  body does nothing except returning zero or null.
    385      * @param delegateObj       the source text specifying the object
    386      *                          that is called on by <code>$proceed()</code>.
    387      * @param delegateMethod    the name of the method
    388      *                          that is called by <code>$proceed()</code>.
    389      */
    390     public void setBody(String src,
    391                         String delegateObj, String delegateMethod)
    392         throws CannotCompileException
    393     {
    394         CtClass cc = declaringClass;
    395         cc.checkModify();
    396         try {
    397             Javac jv = new Javac(cc);
    398             if (delegateMethod != null)
    399                 jv.recordProceed(delegateObj, delegateMethod);
    400 
    401             Bytecode b = jv.compileBody(this, src);
    402             methodInfo.setCodeAttribute(b.toCodeAttribute());
    403             methodInfo.setAccessFlags(methodInfo.getAccessFlags()
    404                                       & ~AccessFlag.ABSTRACT);
    405             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
    406             declaringClass.rebuildClassFile();
    407         }
    408         catch (CompileError e) {
    409             throw new CannotCompileException(e);
    410         } catch (BadBytecode e) {
    411             throw new CannotCompileException(e);
    412         }
    413     }
    414 
    415     static void setBody0(CtClass srcClass, MethodInfo srcInfo,
    416                          CtClass destClass, MethodInfo destInfo,
    417                          ClassMap map)
    418         throws CannotCompileException
    419     {
    420         destClass.checkModify();
    421 
    422         map = new ClassMap(map);
    423         map.put(srcClass.getName(), destClass.getName());
    424         try {
    425             CodeAttribute cattr = srcInfo.getCodeAttribute();
    426             if (cattr != null) {
    427                 ConstPool cp = destInfo.getConstPool();
    428                 CodeAttribute ca = (CodeAttribute)cattr.copy(cp, map);
    429                 destInfo.setCodeAttribute(ca);
    430                 // a stack map table is copied to destInfo.
    431             }
    432         }
    433         catch (CodeAttribute.RuntimeCopyException e) {
    434             /* the exception may be thrown by copy() in CodeAttribute.
    435              */
    436             throw new CannotCompileException(e);
    437         }
    438 
    439         destInfo.setAccessFlags(destInfo.getAccessFlags()
    440                                 & ~AccessFlag.ABSTRACT);
    441         destClass.rebuildClassFile();
    442     }
    443 
    444     /**
    445      * Obtains an attribute with the given name.
    446      * If that attribute is not found in the class file, this
    447      * method returns null.
    448      *
    449      * <p>Note that an attribute is a data block specified by
    450      * the class file format.  It is not an annotation.
    451      * See {@link javassist.bytecode.AttributeInfo}.
    452      *
    453      * @param name              attribute name
    454      */
    455     public byte[] getAttribute(String name) {
    456         AttributeInfo ai = methodInfo.getAttribute(name);
    457         if (ai == null)
    458             return null;
    459         else
    460             return ai.get();
    461     }
    462 
    463     /**
    464      * Adds an attribute. The attribute is saved in the class file.
    465      *
    466      * <p>Note that an attribute is a data block specified by
    467      * the class file format.  It is not an annotation.
    468      * See {@link javassist.bytecode.AttributeInfo}.
    469      *
    470      * @param name      attribute name
    471      * @param data      attribute value
    472      */
    473     public void setAttribute(String name, byte[] data) {
    474         declaringClass.checkModify();
    475         methodInfo.addAttribute(new AttributeInfo(methodInfo.getConstPool(),
    476                                                   name, data));
    477     }
    478 
    479     /**
    480      * Declares to use <code>$cflow</code> for this method/constructor.
    481      * If <code>$cflow</code> is used, the class files modified
    482      * with Javassist requires a support class
    483      * <code>javassist.runtime.Cflow</code> at runtime
    484      * (other Javassist classes are not required at runtime).
    485      *
    486      * <p>Every <code>$cflow</code> variable is given a unique name.
    487      * For example, if the given name is <code>"Point.paint"</code>,
    488      * then the variable is indicated by <code>$cflow(Point.paint)</code>.
    489      *
    490      * @param name      <code>$cflow</code> name.  It can include
    491      *                  alphabets, numbers, <code>_</code>,
    492      *                  <code>$</code>, and <code>.</code> (dot).
    493      *
    494      * @see javassist.runtime.Cflow
    495      */
    496     public void useCflow(String name) throws CannotCompileException {
    497         CtClass cc = declaringClass;
    498         cc.checkModify();
    499         ClassPool pool = cc.getClassPool();
    500         String fname;
    501         int i = 0;
    502         while (true) {
    503             fname = "_cflow$" + i++;
    504             try {
    505                 cc.getDeclaredField(fname);
    506             }
    507             catch(NotFoundException e) {
    508                 break;
    509             }
    510         }
    511 
    512         pool.recordCflow(name, declaringClass.getName(), fname);
    513         try {
    514             CtClass type = pool.get("javassist.runtime.Cflow");
    515             CtField field = new CtField(type, fname, cc);
    516             field.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
    517             cc.addField(field, CtField.Initializer.byNew(type));
    518             insertBefore(fname + ".enter();", false);
    519             String src = fname + ".exit();";
    520             insertAfter(src, true);
    521         }
    522         catch (NotFoundException e) {
    523             throw new CannotCompileException(e);
    524         }
    525     }
    526 
    527     /**
    528      * Declares a new local variable.  The scope of this variable is the
    529      * whole method body.  The initial value of that variable is not set.
    530      * The declared variable can be accessed in the code snippet inserted
    531      * by <code>insertBefore()</code>, <code>insertAfter()</code>, etc.
    532      *
    533      * <p>If the second parameter <code>asFinally</code> to
    534      * <code>insertAfter()</code> is true, the declared local variable
    535      * is not visible from the code inserted by <code>insertAfter()</code>.
    536      *
    537      * @param name      the name of the variable
    538      * @param type      the type of the variable
    539      * @see #insertBefore(String)
    540      * @see #insertAfter(String)
    541      */
    542     public void addLocalVariable(String name, CtClass type)
    543         throws CannotCompileException
    544     {
    545         declaringClass.checkModify();
    546         ConstPool cp = methodInfo.getConstPool();
    547         CodeAttribute ca = methodInfo.getCodeAttribute();
    548         if (ca == null)
    549             throw new CannotCompileException("no method body");
    550 
    551         LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute(
    552                                                 LocalVariableAttribute.tag);
    553         if (va == null) {
    554             va = new LocalVariableAttribute(cp);
    555             ca.getAttributes().add(va);
    556         }
    557 
    558         int maxLocals = ca.getMaxLocals();
    559         String desc = Descriptor.of(type);
    560         va.addEntry(0, ca.getCodeLength(),
    561                     cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals);
    562         ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc));
    563     }
    564 
    565     /**
    566      * Inserts a new parameter, which becomes the first parameter.
    567      */
    568     public void insertParameter(CtClass type)
    569         throws CannotCompileException
    570     {
    571         declaringClass.checkModify();
    572         String desc = methodInfo.getDescriptor();
    573         String desc2 = Descriptor.insertParameter(type, desc);
    574         try {
    575             addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc);
    576         }
    577         catch (BadBytecode e) {
    578             throw new CannotCompileException(e);
    579         }
    580 
    581         methodInfo.setDescriptor(desc2);
    582     }
    583 
    584     /**
    585      * Appends a new parameter, which becomes the last parameter.
    586      */
    587     public void addParameter(CtClass type)
    588         throws CannotCompileException
    589     {
    590         declaringClass.checkModify();
    591         String desc = methodInfo.getDescriptor();
    592         String desc2 = Descriptor.appendParameter(type, desc);
    593         int offset = Modifier.isStatic(getModifiers()) ? 0 : 1;
    594         try {
    595             addParameter2(offset + Descriptor.paramSize(desc), type, desc);
    596         }
    597         catch (BadBytecode e) {
    598             throw new CannotCompileException(e);
    599         }
    600 
    601         methodInfo.setDescriptor(desc2);
    602     }
    603 
    604     private void addParameter2(int where, CtClass type, String desc)
    605         throws BadBytecode
    606     {
    607         CodeAttribute ca = methodInfo.getCodeAttribute();
    608         if (ca != null) {
    609             int size = 1;
    610             char typeDesc = 'L';
    611             int classInfo = 0;
    612             if (type.isPrimitive()) {
    613                 CtPrimitiveType cpt = (CtPrimitiveType)type;
    614                 size = cpt.getDataSize();
    615                 typeDesc = cpt.getDescriptor();
    616             }
    617             else
    618                 classInfo = methodInfo.getConstPool().addClassInfo(type);
    619 
    620             ca.insertLocalVar(where, size);
    621             LocalVariableAttribute va
    622                             = (LocalVariableAttribute)
    623                               ca.getAttribute(LocalVariableAttribute.tag);
    624             if (va != null)
    625                 va.shiftIndex(where, size);
    626 
    627             StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag);
    628             if (smt != null)
    629                 smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
    630 
    631             StackMap sm = (StackMap)ca.getAttribute(StackMap.tag);
    632             if (sm != null)
    633                 sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo);
    634         }
    635     }
    636 
    637     /**
    638      * Modifies the method/constructor body.
    639      *
    640      * @param converter         specifies how to modify.
    641      */
    642     public void instrument(CodeConverter converter)
    643         throws CannotCompileException
    644     {
    645         declaringClass.checkModify();
    646         ConstPool cp = methodInfo.getConstPool();
    647         converter.doit(getDeclaringClass(), methodInfo, cp);
    648     }
    649 
    650     /**
    651      * Modifies the method/constructor body.
    652      *
    653      * @param editor            specifies how to modify.
    654      */
    655     public void instrument(ExprEditor editor)
    656         throws CannotCompileException
    657     {
    658         // if the class is not frozen,
    659         // does not turn the modified flag on.
    660         if (declaringClass.isFrozen())
    661             declaringClass.checkModify();
    662 
    663         if (editor.doit(declaringClass, methodInfo))
    664             declaringClass.checkModify();
    665     }
    666 
    667     /**
    668      * Inserts bytecode at the beginning of the body.
    669      *
    670      * <p>If this object represents a constructor,
    671      * the bytecode is inserted before
    672      * a constructor in the super class or this class is called.
    673      * Therefore, the inserted bytecode is subject to constraints described
    674      * in Section 4.8.2 of The Java Virtual Machine Specification (2nd ed).
    675      * For example, it cannot access instance fields or methods although
    676      * it may assign a value to an instance field directly declared in this
    677      * class.  Accessing static fields and methods is allowed.
    678      * Use <code>insertBeforeBody()</code> in <code>CtConstructor</code>.
    679      *
    680      * @param src       the source code representing the inserted bytecode.
    681      *                  It must be a single statement or block.
    682      * @see CtConstructor#insertBeforeBody(String)
    683      */
    684     public void insertBefore(String src) throws CannotCompileException {
    685         insertBefore(src, true);
    686     }
    687 
    688     private void insertBefore(String src, boolean rebuild)
    689         throws CannotCompileException
    690     {
    691         CtClass cc = declaringClass;
    692         cc.checkModify();
    693         CodeAttribute ca = methodInfo.getCodeAttribute();
    694         if (ca == null)
    695             throw new CannotCompileException("no method body");
    696 
    697         CodeIterator iterator = ca.iterator();
    698         Javac jv = new Javac(cc);
    699         try {
    700             int nvars = jv.recordParams(getParameterTypes(),
    701                                         Modifier.isStatic(getModifiers()));
    702             jv.recordParamNames(ca, nvars);
    703             jv.recordLocalVariables(ca, 0);
    704             jv.recordType(getReturnType0());
    705             jv.compileStmnt(src);
    706             Bytecode b = jv.getBytecode();
    707             int stack = b.getMaxStack();
    708             int locals = b.getMaxLocals();
    709 
    710             if (stack > ca.getMaxStack())
    711                 ca.setMaxStack(stack);
    712 
    713             if (locals > ca.getMaxLocals())
    714                 ca.setMaxLocals(locals);
    715 
    716             int pos = iterator.insertEx(b.get());
    717             iterator.insert(b.getExceptionTable(), pos);
    718             if (rebuild)
    719                 methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
    720         }
    721         catch (NotFoundException e) {
    722             throw new CannotCompileException(e);
    723         }
    724         catch (CompileError e) {
    725             throw new CannotCompileException(e);
    726         }
    727         catch (BadBytecode e) {
    728             throw new CannotCompileException(e);
    729         }
    730     }
    731 
    732     /**
    733      * Inserts bytecode at the end of the body.
    734      * The bytecode is inserted just before every return insturction.
    735      * It is not executed when an exception is thrown.
    736      *
    737      * @param src       the source code representing the inserted bytecode.
    738      *                  It must be a single statement or block.
    739      */
    740     public void insertAfter(String src)
    741         throws CannotCompileException
    742     {
    743         insertAfter(src, false);
    744     }
    745 
    746     /**
    747      * Inserts bytecode at the end of the body.
    748      * The bytecode is inserted just before every return insturction.
    749      *
    750      * @param src       the source code representing the inserted bytecode.
    751      *                  It must be a single statement or block.
    752      * @param asFinally         true if the inserted bytecode is executed
    753      *                  not only when the control normally returns
    754      *                  but also when an exception is thrown.
    755      *                  If this parameter is true, the inserted code cannot
    756      *                  access local variables.
    757      */
    758     public void insertAfter(String src, boolean asFinally)
    759         throws CannotCompileException
    760     {
    761         CtClass cc = declaringClass;
    762         cc.checkModify();
    763         ConstPool pool = methodInfo.getConstPool();
    764         CodeAttribute ca = methodInfo.getCodeAttribute();
    765         if (ca == null)
    766             throw new CannotCompileException("no method body");
    767 
    768         CodeIterator iterator = ca.iterator();
    769         int retAddr = ca.getMaxLocals();
    770         Bytecode b = new Bytecode(pool, 0, retAddr + 1);
    771         b.setStackDepth(ca.getMaxStack() + 1);
    772         Javac jv = new Javac(b, cc);
    773         try {
    774             int nvars = jv.recordParams(getParameterTypes(),
    775                                         Modifier.isStatic(getModifiers()));
    776             jv.recordParamNames(ca, nvars);
    777             CtClass rtype = getReturnType0();
    778             int varNo = jv.recordReturnType(rtype, true);
    779             jv.recordLocalVariables(ca, 0);
    780 
    781             // finally clause for exceptions
    782             int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo,
    783                                                 jv, src);
    784             // finally clause for normal termination
    785             insertAfterAdvice(b, jv, src, pool, rtype, varNo);
    786 
    787             ca.setMaxStack(b.getMaxStack());
    788             ca.setMaxLocals(b.getMaxLocals());
    789 
    790             int gapPos = iterator.append(b.get());
    791             iterator.append(b.getExceptionTable(), gapPos);
    792 
    793             if (asFinally)
    794                 ca.getExceptionTable().add(getStartPosOfBody(ca), gapPos, gapPos, 0);
    795 
    796             int gapLen = iterator.getCodeLength() - gapPos - handlerLen;
    797             int subr = iterator.getCodeLength() - gapLen;
    798 
    799             while (iterator.hasNext()) {
    800                 int pos = iterator.next();
    801                 if (pos >= subr)
    802                     break;
    803 
    804                 int c = iterator.byteAt(pos);
    805                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
    806                     || c == Opcode.FRETURN || c == Opcode.LRETURN
    807                     || c == Opcode.DRETURN || c == Opcode.RETURN) {
    808                     insertGoto(iterator, subr, pos);
    809                     subr = iterator.getCodeLength() - gapLen;
    810                 }
    811             }
    812 
    813             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
    814         }
    815         catch (NotFoundException e) {
    816             throw new CannotCompileException(e);
    817         }
    818         catch (CompileError e) {
    819             throw new CannotCompileException(e);
    820         }
    821         catch (BadBytecode e) {
    822             throw new CannotCompileException(e);
    823         }
    824     }
    825 
    826     private void insertAfterAdvice(Bytecode code, Javac jv, String src,
    827                                    ConstPool cp, CtClass rtype, int varNo)
    828         throws CompileError
    829     {
    830         if (rtype == CtClass.voidType) {
    831             code.addOpcode(Opcode.ACONST_NULL);
    832             code.addAstore(varNo);
    833             jv.compileStmnt(src);
    834             code.addOpcode(Opcode.RETURN);
    835             if (code.getMaxLocals() < 1)
    836                 code.setMaxLocals(1);
    837         }
    838         else {
    839             code.addStore(varNo, rtype);
    840             jv.compileStmnt(src);
    841             code.addLoad(varNo, rtype);
    842             if (rtype.isPrimitive())
    843                 code.addOpcode(((CtPrimitiveType)rtype).getReturnOp());
    844             else
    845                 code.addOpcode(Opcode.ARETURN);
    846         }
    847     }
    848 
    849     /*
    850      * assert subr > pos
    851      */
    852     private void insertGoto(CodeIterator iterator, int subr, int pos)
    853         throws BadBytecode
    854     {
    855         iterator.setMark(subr);
    856         // the gap length might be a multiple of 4.
    857         iterator.writeByte(Opcode.NOP, pos);
    858         boolean wide = subr + 2 - pos > Short.MAX_VALUE;
    859         pos = iterator.insertGapAt(pos, wide ? 4 : 2, false).position;
    860         int offset = iterator.getMark() - pos;
    861         if (wide) {
    862             iterator.writeByte(Opcode.GOTO_W, pos);
    863             iterator.write32bit(offset, pos + 1);
    864         }
    865         else if (offset <= Short.MAX_VALUE) {
    866             iterator.writeByte(Opcode.GOTO, pos);
    867             iterator.write16bit(offset, pos + 1);
    868         }
    869         else {
    870             pos = iterator.insertGapAt(pos, 2, false).position;
    871             iterator.writeByte(Opcode.GOTO_W, pos);
    872             iterator.write32bit(iterator.getMark() - pos, pos + 1);
    873         }
    874     }
    875 
    876     /* insert a finally clause
    877      */
    878     private int insertAfterHandler(boolean asFinally, Bytecode b,
    879                                    CtClass rtype, int returnVarNo,
    880                                    Javac javac, String src)
    881         throws CompileError
    882     {
    883         if (!asFinally)
    884             return 0;
    885 
    886         int var = b.getMaxLocals();
    887         b.incMaxLocals(1);
    888         int pc = b.currentPc();
    889         b.addAstore(var);   // store an exception
    890         if (rtype.isPrimitive()) {
    891             char c = ((CtPrimitiveType)rtype).getDescriptor();
    892             if (c == 'D') {
    893                 b.addDconst(0.0);
    894                 b.addDstore(returnVarNo);
    895             }
    896             else if (c == 'F') {
    897                 b.addFconst(0);
    898                 b.addFstore(returnVarNo);
    899             }
    900             else if (c == 'J') {
    901                 b.addLconst(0);
    902                 b.addLstore(returnVarNo);
    903             }
    904             else if (c == 'V') {
    905                 b.addOpcode(Opcode.ACONST_NULL);
    906                 b.addAstore(returnVarNo);
    907             }
    908             else { // int, boolean, char, short, ...
    909                 b.addIconst(0);
    910                 b.addIstore(returnVarNo);
    911             }
    912         }
    913         else {
    914             b.addOpcode(Opcode.ACONST_NULL);
    915             b.addAstore(returnVarNo);
    916         }
    917 
    918         javac.compileStmnt(src);
    919         b.addAload(var);
    920         b.addOpcode(Opcode.ATHROW);
    921         return b.currentPc() - pc;
    922     }
    923 
    924     /* -- OLD version --
    925 
    926     public void insertAfter(String src) throws CannotCompileException {
    927         declaringClass.checkModify();
    928         CodeAttribute ca = methodInfo.getCodeAttribute();
    929         CodeIterator iterator = ca.iterator();
    930         Bytecode b = new Bytecode(methodInfo.getConstPool(),
    931                                   ca.getMaxStack(), ca.getMaxLocals());
    932         b.setStackDepth(ca.getMaxStack());
    933         Javac jv = new Javac(b, declaringClass);
    934         try {
    935             jv.recordParams(getParameterTypes(),
    936                             Modifier.isStatic(getModifiers()));
    937             CtClass rtype = getReturnType0();
    938             int varNo = jv.recordReturnType(rtype, true);
    939             boolean isVoid = rtype == CtClass.voidType;
    940             if (isVoid) {
    941                 b.addOpcode(Opcode.ACONST_NULL);
    942                 b.addAstore(varNo);
    943                 jv.compileStmnt(src);
    944             }
    945             else {
    946                 b.addStore(varNo, rtype);
    947                 jv.compileStmnt(src);
    948                 b.addLoad(varNo, rtype);
    949             }
    950 
    951             byte[] code = b.get();
    952             ca.setMaxStack(b.getMaxStack());
    953             ca.setMaxLocals(b.getMaxLocals());
    954             while (iterator.hasNext()) {
    955                 int pos = iterator.next();
    956                 int c = iterator.byteAt(pos);
    957                 if (c == Opcode.ARETURN || c == Opcode.IRETURN
    958                     || c == Opcode.FRETURN || c == Opcode.LRETURN
    959                     || c == Opcode.DRETURN || c == Opcode.RETURN)
    960                     iterator.insert(pos, code);
    961             }
    962         }
    963         catch (NotFoundException e) {
    964             throw new CannotCompileException(e);
    965         }
    966         catch (CompileError e) {
    967             throw new CannotCompileException(e);
    968         }
    969         catch (BadBytecode e) {
    970             throw new CannotCompileException(e);
    971         }
    972     }
    973     */
    974 
    975     /**
    976      * Adds a catch clause that handles an exception thrown in the
    977      * body.  The catch clause must end with a return or throw statement.
    978      *
    979      * @param src       the source code representing the catch clause.
    980      *                  It must be a single statement or block.
    981      * @param exceptionType     the type of the exception handled by the
    982      *                          catch clause.
    983      */
    984     public void addCatch(String src, CtClass exceptionType)
    985         throws CannotCompileException
    986     {
    987         addCatch(src, exceptionType, "$e");
    988     }
    989 
    990     /**
    991      * Adds a catch clause that handles an exception thrown in the
    992      * body.  The catch clause must end with a return or throw statement.
    993      *
    994      * @param src       the source code representing the catch clause.
    995      *                  It must be a single statement or block.
    996      * @param exceptionType     the type of the exception handled by the
    997      *                          catch clause.
    998      * @param exceptionName     the name of the variable containing the
    999      *                          caught exception, for example,
   1000      *                          <code>$e</code>.
   1001      */
   1002     public void addCatch(String src, CtClass exceptionType,
   1003                          String exceptionName)
   1004         throws CannotCompileException
   1005     {
   1006         CtClass cc = declaringClass;
   1007         cc.checkModify();
   1008         ConstPool cp = methodInfo.getConstPool();
   1009         CodeAttribute ca = methodInfo.getCodeAttribute();
   1010         CodeIterator iterator = ca.iterator();
   1011         Bytecode b = new Bytecode(cp, ca.getMaxStack(), ca.getMaxLocals());
   1012         b.setStackDepth(1);
   1013         Javac jv = new Javac(b, cc);
   1014         try {
   1015             jv.recordParams(getParameterTypes(),
   1016                             Modifier.isStatic(getModifiers()));
   1017             int var = jv.recordVariable(exceptionType, exceptionName);
   1018             b.addAstore(var);
   1019             jv.compileStmnt(src);
   1020 
   1021             int stack = b.getMaxStack();
   1022             int locals = b.getMaxLocals();
   1023 
   1024             if (stack > ca.getMaxStack())
   1025                 ca.setMaxStack(stack);
   1026 
   1027             if (locals > ca.getMaxLocals())
   1028                 ca.setMaxLocals(locals);
   1029 
   1030             int len = iterator.getCodeLength();
   1031             int pos = iterator.append(b.get());
   1032             ca.getExceptionTable().add(getStartPosOfBody(ca), len, len,
   1033                                        cp.addClassInfo(exceptionType));
   1034             iterator.append(b.getExceptionTable(), pos);
   1035             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
   1036         }
   1037         catch (NotFoundException e) {
   1038             throw new CannotCompileException(e);
   1039         }
   1040         catch (CompileError e) {
   1041             throw new CannotCompileException(e);
   1042         } catch (BadBytecode e) {
   1043             throw new CannotCompileException(e);
   1044         }
   1045     }
   1046 
   1047     /* CtConstructor overrides this method.
   1048      */
   1049     int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException {
   1050         return 0;
   1051     }
   1052 
   1053     /**
   1054      * Inserts bytecode at the specified line in the body.
   1055      * It is equivalent to:
   1056      *
   1057      * <br><code>insertAt(lineNum, true, src)</code>
   1058      *
   1059      * <br>See this method as well.
   1060      *
   1061      * @param lineNum   the line number.  The bytecode is inserted at the
   1062      *                  beginning of the code at the line specified by this
   1063      *                  line number.
   1064      * @param src       the source code representing the inserted bytecode.
   1065      *                  It must be a single statement or block.
   1066      * @return      the line number at which the bytecode has been inserted.
   1067      *
   1068      * @see CtBehavior#insertAt(int,boolean,String)
   1069      */
   1070     public int insertAt(int lineNum, String src)
   1071         throws CannotCompileException
   1072     {
   1073         return insertAt(lineNum, true, src);
   1074     }
   1075 
   1076     /**
   1077      * Inserts bytecode at the specified line in the body.
   1078      *
   1079      * <p>If there is not
   1080      * a statement at the specified line, the bytecode might be inserted
   1081      * at the line including the first statement after that line specified.
   1082      * For example, if there is only a closing brace at that line, the
   1083      * bytecode would be inserted at another line below.
   1084      * To know exactly where the bytecode will be inserted, call with
   1085      * <code>modify</code> set to <code>false</code>.
   1086      *
   1087      * @param lineNum   the line number.  The bytecode is inserted at the
   1088      *                  beginning of the code at the line specified by this
   1089      *                  line number.
   1090      * @param modify    if false, this method does not insert the bytecode.
   1091      *                  It instead only returns the line number at which
   1092      *                  the bytecode would be inserted.
   1093      * @param src       the source code representing the inserted bytecode.
   1094      *                  It must be a single statement or block.
   1095      *                  If modify is false, the value of src can be null.
   1096      * @return      the line number at which the bytecode has been inserted.
   1097      */
   1098     public int insertAt(int lineNum, boolean modify, String src)
   1099         throws CannotCompileException
   1100     {
   1101         CodeAttribute ca = methodInfo.getCodeAttribute();
   1102         if (ca == null)
   1103             throw new CannotCompileException("no method body");
   1104 
   1105         LineNumberAttribute ainfo
   1106             = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag);
   1107         if (ainfo == null)
   1108             throw new CannotCompileException("no line number info");
   1109 
   1110         LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum);
   1111         lineNum = pc.line;
   1112         int index = pc.index;
   1113         if (!modify)
   1114             return lineNum;
   1115 
   1116         CtClass cc = declaringClass;
   1117         cc.checkModify();
   1118         CodeIterator iterator = ca.iterator();
   1119         Javac jv = new Javac(cc);
   1120         try {
   1121             jv.recordLocalVariables(ca, index);
   1122             jv.recordParams(getParameterTypes(),
   1123                             Modifier.isStatic(getModifiers()));
   1124             jv.setMaxLocals(ca.getMaxLocals());
   1125             jv.compileStmnt(src);
   1126             Bytecode b = jv.getBytecode();
   1127             int locals = b.getMaxLocals();
   1128             int stack = b.getMaxStack();
   1129             ca.setMaxLocals(locals);
   1130 
   1131             /* We assume that there is no values in the operand stack
   1132              * at the position where the bytecode is inserted.
   1133              */
   1134             if (stack > ca.getMaxStack())
   1135                 ca.setMaxStack(stack);
   1136 
   1137             index = iterator.insertAt(index, b.get());
   1138             iterator.insert(b.getExceptionTable(), index);
   1139             methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2());
   1140             return lineNum;
   1141         }
   1142         catch (NotFoundException e) {
   1143             throw new CannotCompileException(e);
   1144         }
   1145         catch (CompileError e) {
   1146             throw new CannotCompileException(e);
   1147         }
   1148         catch (BadBytecode e) {
   1149             throw new CannotCompileException(e);
   1150         }
   1151     }
   1152 }
   1153