Home | History | Annotate | Download | only in bytecode
      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.bytecode;
     17 
     18 import java.io.DataInputStream;
     19 import java.io.DataOutputStream;
     20 import java.io.IOException;
     21 import java.util.ArrayList;
     22 import java.util.List;
     23 import java.util.Map;
     24 import javassist.ClassPool;
     25 import javassist.bytecode.stackmap.MapMaker;
     26 
     27 /**
     28  * <code>method_info</code> structure.
     29  *
     30  * @see javassist.CtMethod#getMethodInfo()
     31  * @see javassist.CtConstructor#getMethodInfo()
     32  */
     33 public class MethodInfo {
     34     ConstPool constPool;
     35     int accessFlags;
     36     int name;
     37     String cachedName;
     38     int descriptor;
     39     ArrayList attribute; // may be null
     40 
     41     /**
     42      * If this value is true, Javassist maintains a <code>StackMap</code> attribute
     43      * generated by the <code>preverify</code> tool of J2ME (CLDC).  The initial
     44      * value of this field is <code>false</code>.
     45      */
     46     public static boolean doPreverify = false;
     47 
     48     /**
     49      * The name of constructors: <code>&lt;init&gt</code>.
     50      */
     51     public static final String nameInit = "<init>";
     52 
     53     /**
     54      * The name of class initializer (static initializer):
     55      * <code>&lt;clinit&gt</code>.
     56      */
     57     public static final String nameClinit = "<clinit>";
     58 
     59     private MethodInfo(ConstPool cp) {
     60         constPool = cp;
     61         attribute = null;
     62     }
     63 
     64     /**
     65      * Constructs a <code>method_info</code> structure. The initial value of
     66      * <code>access_flags</code> is zero.
     67      *
     68      * @param cp
     69      *            a constant pool table
     70      * @param methodname
     71      *            method name
     72      * @param desc
     73      *            method descriptor
     74      * @see Descriptor
     75      */
     76     public MethodInfo(ConstPool cp, String methodname, String desc) {
     77         this(cp);
     78         accessFlags = 0;
     79         name = cp.addUtf8Info(methodname);
     80         cachedName = methodname;
     81         descriptor = constPool.addUtf8Info(desc);
     82     }
     83 
     84     MethodInfo(ConstPool cp, DataInputStream in) throws IOException {
     85         this(cp);
     86         read(in);
     87     }
     88 
     89     /**
     90      * Constructs a copy of <code>method_info</code> structure. Class names
     91      * appearing in the source <code>method_info</code> are renamed according
     92      * to <code>classnameMap</code>.
     93      *
     94      * <p>
     95      * Note: only <code>Code</code> and <code>Exceptions</code> attributes
     96      * are copied from the source. The other attributes are ignored.
     97      *
     98      * @param cp
     99      *            a constant pool table
    100      * @param methodname
    101      *            a method name
    102      * @param src
    103      *            a source <code>method_info</code>
    104      * @param classnameMap
    105      *            specifies pairs of replaced and substituted name.
    106      * @see Descriptor
    107      */
    108     public MethodInfo(ConstPool cp, String methodname, MethodInfo src,
    109             Map classnameMap) throws BadBytecode {
    110         this(cp);
    111         read(src, methodname, classnameMap);
    112     }
    113 
    114     /**
    115      * Returns a string representation of the object.
    116      */
    117     public String toString() {
    118         return getName() + " " + getDescriptor();
    119     }
    120 
    121     /**
    122      * Copies all constant pool items to a given new constant pool
    123      * and replaces the original items with the new ones.
    124      * This is used for garbage collecting the items of removed fields
    125      * and methods.
    126      *
    127      * @param cp    the destination
    128      */
    129     void compact(ConstPool cp) {
    130         name = cp.addUtf8Info(getName());
    131         descriptor = cp.addUtf8Info(getDescriptor());
    132         attribute = AttributeInfo.copyAll(attribute, cp);
    133         constPool = cp;
    134     }
    135 
    136     void prune(ConstPool cp) {
    137         ArrayList newAttributes = new ArrayList();
    138 
    139         AttributeInfo invisibleAnnotations
    140             = getAttribute(AnnotationsAttribute.invisibleTag);
    141         if (invisibleAnnotations != null) {
    142             invisibleAnnotations = invisibleAnnotations.copy(cp, null);
    143             newAttributes.add(invisibleAnnotations);
    144         }
    145 
    146         AttributeInfo visibleAnnotations
    147             = getAttribute(AnnotationsAttribute.visibleTag);
    148         if (visibleAnnotations != null) {
    149             visibleAnnotations = visibleAnnotations.copy(cp, null);
    150             newAttributes.add(visibleAnnotations);
    151         }
    152 
    153         AttributeInfo parameterInvisibleAnnotations
    154             = getAttribute(ParameterAnnotationsAttribute.invisibleTag);
    155         if (parameterInvisibleAnnotations != null) {
    156             parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null);
    157             newAttributes.add(parameterInvisibleAnnotations);
    158         }
    159 
    160         AttributeInfo parameterVisibleAnnotations
    161             = getAttribute(ParameterAnnotationsAttribute.visibleTag);
    162         if (parameterVisibleAnnotations != null) {
    163             parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null);
    164             newAttributes.add(parameterVisibleAnnotations);
    165         }
    166 
    167         AnnotationDefaultAttribute defaultAttribute
    168              = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag);
    169         if (defaultAttribute != null)
    170             newAttributes.add(defaultAttribute);
    171 
    172         ExceptionsAttribute ea = getExceptionsAttribute();
    173         if (ea != null)
    174             newAttributes.add(ea);
    175 
    176         AttributeInfo signature
    177             = getAttribute(SignatureAttribute.tag);
    178         if (signature != null) {
    179             signature = signature.copy(cp, null);
    180             newAttributes.add(signature);
    181         }
    182 
    183         attribute = newAttributes;
    184         name = cp.addUtf8Info(getName());
    185         descriptor = cp.addUtf8Info(getDescriptor());
    186         constPool = cp;
    187     }
    188 
    189     /**
    190      * Returns a method name.
    191      */
    192     public String getName() {
    193        if (cachedName == null)
    194            cachedName = constPool.getUtf8Info(name);
    195 
    196        return cachedName;
    197     }
    198 
    199     /**
    200      * Sets a method name.
    201      */
    202     public void setName(String newName) {
    203         name = constPool.addUtf8Info(newName);
    204         cachedName = newName;
    205     }
    206 
    207     /**
    208      * Returns true if this is not a constructor or a class initializer (static
    209      * initializer).
    210      */
    211     public boolean isMethod() {
    212         String n = getName();
    213         return !n.equals(nameInit) && !n.equals(nameClinit);
    214     }
    215 
    216     /**
    217      * Returns a constant pool table used by this method.
    218      */
    219     public ConstPool getConstPool() {
    220         return constPool;
    221     }
    222 
    223     /**
    224      * Returns true if this is a constructor.
    225      */
    226     public boolean isConstructor() {
    227         return getName().equals(nameInit);
    228     }
    229 
    230     /**
    231      * Returns true if this is a class initializer (static initializer).
    232      */
    233     public boolean isStaticInitializer() {
    234         return getName().equals(nameClinit);
    235     }
    236 
    237     /**
    238      * Returns access flags.
    239      *
    240      * @see AccessFlag
    241      */
    242     public int getAccessFlags() {
    243         return accessFlags;
    244     }
    245 
    246     /**
    247      * Sets access flags.
    248      *
    249      * @see AccessFlag
    250      */
    251     public void setAccessFlags(int acc) {
    252         accessFlags = acc;
    253     }
    254 
    255     /**
    256      * Returns a method descriptor.
    257      *
    258      * @see Descriptor
    259      */
    260     public String getDescriptor() {
    261         return constPool.getUtf8Info(descriptor);
    262     }
    263 
    264     /**
    265      * Sets a method descriptor.
    266      *
    267      * @see Descriptor
    268      */
    269     public void setDescriptor(String desc) {
    270         if (!desc.equals(getDescriptor()))
    271             descriptor = constPool.addUtf8Info(desc);
    272     }
    273 
    274     /**
    275      * Returns all the attributes.  The returned <code>List</code> object
    276      * is shared with this object.  If you add a new attribute to the list,
    277      * the attribute is also added to the method represented by this
    278      * object.  If you remove an attribute from the list, it is also removed
    279      * from the method.
    280      *
    281      * @return a list of <code>AttributeInfo</code> objects.
    282      * @see AttributeInfo
    283      */
    284     public List getAttributes() {
    285         if (attribute == null)
    286             attribute = new ArrayList();
    287 
    288         return attribute;
    289     }
    290 
    291     /**
    292      * Returns the attribute with the specified name. If it is not found, this
    293      * method returns null.
    294      *
    295      * @param name attribute name
    296      * @return an <code>AttributeInfo</code> object or null.
    297      * @see #getAttributes()
    298      */
    299     public AttributeInfo getAttribute(String name) {
    300         return AttributeInfo.lookup(attribute, name);
    301     }
    302 
    303     /**
    304      * Appends an attribute. If there is already an attribute with the same
    305      * name, the new one substitutes for it.
    306      *
    307      * @see #getAttributes()
    308      */
    309     public void addAttribute(AttributeInfo info) {
    310         if (attribute == null)
    311             attribute = new ArrayList();
    312 
    313         AttributeInfo.remove(attribute, info.getName());
    314         attribute.add(info);
    315     }
    316 
    317     /**
    318      * Returns an Exceptions attribute.
    319      *
    320      * @return an Exceptions attribute or null if it is not specified.
    321      */
    322     public ExceptionsAttribute getExceptionsAttribute() {
    323         AttributeInfo info = AttributeInfo.lookup(attribute,
    324                 ExceptionsAttribute.tag);
    325         return (ExceptionsAttribute)info;
    326     }
    327 
    328     /**
    329      * Returns a Code attribute.
    330      *
    331      * @return a Code attribute or null if it is not specified.
    332      */
    333     public CodeAttribute getCodeAttribute() {
    334         AttributeInfo info = AttributeInfo.lookup(attribute, CodeAttribute.tag);
    335         return (CodeAttribute)info;
    336     }
    337 
    338     /**
    339      * Removes an Exception attribute.
    340      */
    341     public void removeExceptionsAttribute() {
    342         AttributeInfo.remove(attribute, ExceptionsAttribute.tag);
    343     }
    344 
    345     /**
    346      * Adds an Exception attribute.
    347      *
    348      * <p>
    349      * The added attribute must share the same constant pool table as this
    350      * <code>method_info</code> structure.
    351      */
    352     public void setExceptionsAttribute(ExceptionsAttribute cattr) {
    353         removeExceptionsAttribute();
    354         if (attribute == null)
    355             attribute = new ArrayList();
    356 
    357         attribute.add(cattr);
    358     }
    359 
    360     /**
    361      * Removes a Code attribute.
    362      */
    363     public void removeCodeAttribute() {
    364         AttributeInfo.remove(attribute, CodeAttribute.tag);
    365     }
    366 
    367     /**
    368      * Adds a Code attribute.
    369      *
    370      * <p>
    371      * The added attribute must share the same constant pool table as this
    372      * <code>method_info</code> structure.
    373      */
    374     public void setCodeAttribute(CodeAttribute cattr) {
    375         removeCodeAttribute();
    376         if (attribute == null)
    377             attribute = new ArrayList();
    378 
    379         attribute.add(cattr);
    380     }
    381 
    382     /**
    383      * Rebuilds a stack map table if the class file is for Java 6
    384      * or later.  Java 5 or older Java VMs do not recognize a stack
    385      * map table.  If <code>doPreverify</code> is true, this method
    386      * also rebuilds a stack map for J2ME (CLDC).
    387      *
    388      * @param pool          used for making type hierarchy.
    389      * @param cf            rebuild if this class file is for Java 6 or later.
    390      * @see #rebuildStackMap(ClassPool)
    391      * @see #rebuildStackMapForME(ClassPool)
    392      * @since 3.6
    393      */
    394     public void rebuildStackMapIf6(ClassPool pool, ClassFile cf)
    395         throws BadBytecode
    396     {
    397         if (cf.getMajorVersion() >= ClassFile.JAVA_6)
    398             rebuildStackMap(pool);
    399 
    400         if (doPreverify)
    401             rebuildStackMapForME(pool);
    402     }
    403 
    404     /**
    405      * Rebuilds a stack map table.  If no stack map table is included,
    406      * a new one is created.  If this <code>MethodInfo</code> does not
    407      * include a code attribute, nothing happens.
    408      *
    409      * @param pool          used for making type hierarchy.
    410      * @see StackMapTable
    411      * @since 3.6
    412      */
    413     public void rebuildStackMap(ClassPool pool) throws BadBytecode {
    414         CodeAttribute ca = getCodeAttribute();
    415         if (ca != null) {
    416             StackMapTable smt = MapMaker.make(pool, this);
    417             ca.setAttribute(smt);
    418         }
    419     }
    420 
    421     /**
    422      * Rebuilds a stack map table for J2ME (CLDC).  If no stack map table is included,
    423      * a new one is created.  If this <code>MethodInfo</code> does not
    424      * include a code attribute, nothing happens.
    425      *
    426      * @param pool          used for making type hierarchy.
    427      * @see StackMapTable
    428      * @since 3.12
    429      */
    430     public void rebuildStackMapForME(ClassPool pool) throws BadBytecode {
    431         CodeAttribute ca = getCodeAttribute();
    432         if (ca != null) {
    433             StackMap sm = MapMaker.make2(pool, this);
    434             ca.setAttribute(sm);
    435         }
    436     }
    437 
    438     /**
    439      * Returns the line number of the source line corresponding to the specified
    440      * bytecode contained in this method.
    441      *
    442      * @param pos
    443      *            the position of the bytecode (&gt;= 0). an index into the code
    444      *            array.
    445      * @return -1 if this information is not available.
    446      */
    447     public int getLineNumber(int pos) {
    448         CodeAttribute ca = getCodeAttribute();
    449         if (ca == null)
    450             return -1;
    451 
    452         LineNumberAttribute ainfo = (LineNumberAttribute)ca
    453                 .getAttribute(LineNumberAttribute.tag);
    454         if (ainfo == null)
    455             return -1;
    456 
    457         return ainfo.toLineNumber(pos);
    458     }
    459 
    460     /**
    461      * Changes a super constructor called by this constructor.
    462      *
    463      * <p>
    464      * This method modifies a call to <code>super()</code>, which should be
    465      * at the head of a constructor body, so that a constructor in a different
    466      * super class is called. This method does not change actual parameters.
    467      * Hence the new super class must have a constructor with the same signature
    468      * as the original one.
    469      *
    470      * <p>
    471      * This method should be called when the super class of the class declaring
    472      * this method is changed.
    473      *
    474      * <p>
    475      * This method does not perform anything unless this <code>MethodInfo</code>
    476      * represents a constructor.
    477      *
    478      * @param superclass
    479      *            the new super class
    480      */
    481     public void setSuperclass(String superclass) throws BadBytecode {
    482         if (!isConstructor())
    483             return;
    484 
    485         CodeAttribute ca = getCodeAttribute();
    486         byte[] code = ca.getCode();
    487         CodeIterator iterator = ca.iterator();
    488         int pos = iterator.skipSuperConstructor();
    489         if (pos >= 0) { // not this()
    490             ConstPool cp = constPool;
    491             int mref = ByteArray.readU16bit(code, pos + 1);
    492             int nt = cp.getMethodrefNameAndType(mref);
    493             int sc = cp.addClassInfo(superclass);
    494             int mref2 = cp.addMethodrefInfo(sc, nt);
    495             ByteArray.write16bit(mref2, code, pos + 1);
    496         }
    497     }
    498 
    499     private void read(MethodInfo src, String methodname, Map classnames)
    500             throws BadBytecode {
    501         ConstPool destCp = constPool;
    502         accessFlags = src.accessFlags;
    503         name = destCp.addUtf8Info(methodname);
    504         cachedName = methodname;
    505         ConstPool srcCp = src.constPool;
    506         String desc = srcCp.getUtf8Info(src.descriptor);
    507         String desc2 = Descriptor.rename(desc, classnames);
    508         descriptor = destCp.addUtf8Info(desc2);
    509 
    510         attribute = new ArrayList();
    511         ExceptionsAttribute eattr = src.getExceptionsAttribute();
    512         if (eattr != null)
    513             attribute.add(eattr.copy(destCp, classnames));
    514 
    515         CodeAttribute cattr = src.getCodeAttribute();
    516         if (cattr != null)
    517             attribute.add(cattr.copy(destCp, classnames));
    518     }
    519 
    520     private void read(DataInputStream in) throws IOException {
    521         accessFlags = in.readUnsignedShort();
    522         name = in.readUnsignedShort();
    523         descriptor = in.readUnsignedShort();
    524         int n = in.readUnsignedShort();
    525         attribute = new ArrayList();
    526         for (int i = 0; i < n; ++i)
    527             attribute.add(AttributeInfo.read(constPool, in));
    528     }
    529 
    530     void write(DataOutputStream out) throws IOException {
    531         out.writeShort(accessFlags);
    532         out.writeShort(name);
    533         out.writeShort(descriptor);
    534 
    535         if (attribute == null)
    536             out.writeShort(0);
    537         else {
    538             out.writeShort(attribute.size());
    539             AttributeInfo.writeAll(attribute, out);
    540         }
    541     }
    542 }
    543