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.util.Map;
     19 import java.util.HashMap;
     20 import java.io.IOException;
     21 import java.io.DataInputStream;
     22 import java.io.ByteArrayOutputStream;
     23 import javassist.bytecode.annotation.*;
     24 
     25 /**
     26  * A class representing
     27  * <code>RuntimeVisibleAnnotations_attribute</code> and
     28  * <code>RuntimeInvisibleAnnotations_attribute</code>.
     29  *
     30  * <p>To obtain an AnnotationAttribute object, invoke
     31  * <code>getAttribute(AnnotationsAttribute.visibleTag)</code>
     32  * in <code>ClassFile</code>, <code>MethodInfo</code>,
     33  * or <code>FieldInfo</code>.  The obtained attribute is a
     34  * runtime visible annotations attribute.
     35  * If the parameter is
     36  * <code>AnnotationAttribute.invisibleTag</code>, then the obtained
     37  * attribute is a runtime invisible one.
     38  *
     39  * <p>For example,
     40  *
     41  * <ul><pre>
     42  * import javassist.bytecode.annotation.Annotation;
     43  *    :
     44  * CtMethod m = ... ;
     45  * MethodInfo minfo = m.getMethodInfo();
     46  * AnnotationsAttribute attr = (AnnotationsAttribute)
     47  *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
     48  * Annotation an = attr.getAnnotation("Author");
     49  * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
     50  * System.out.println("@Author(name=" + s + ")");
     51  * </pre></ul>
     52  *
     53  * <p>This code snippet retrieves an annotation of the type <code>Author</code>
     54  * from the <code>MethodInfo</code> object specified by <code>minfo</code>.
     55  * Then, it prints the value of <code>name</code> in <code>Author</code>.
     56  *
     57  * <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
     58  *
     59  * <ul><pre>
     60  * &#64;Retention(RetentionPolicy.RUNTIME)
     61  * </pre></ul>
     62  *
     63  * <p>Then <code>Author</code> is visible at runtime.  Therefore, the third
     64  * statement of the code snippet above must be changed into:
     65  *
     66  * <ul><pre>
     67  * AnnotationsAttribute attr = (AnnotationsAttribute)
     68  *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
     69  * </pre></ul>
     70  *
     71  * <p>The attribute tag must be <code>visibleTag</code> instead of
     72  * <code>invisibleTag</code>.
     73  *
     74  * <p>If the member value of an annotation is not specified, the default value
     75  * is used as that member value.  If so, <code>getMemberValue()</code> in
     76  * <code>Annotation</code> returns <code>null</code>
     77  * since the default value is not included in the
     78  * <code>AnnotationsAttribute</code>.  It is included in the
     79  * <code>AnnotationDefaultAttribute</code> of the method declared in the
     80  * annotation type.
     81  *
     82  * <p>If you want to record a new AnnotationAttribute object, execute the
     83  * following snippet:
     84  *
     85  * <ul><pre>
     86  * ClassFile cf = ... ;
     87  * ConstPool cp = cf.getConstPool();
     88  * AnnotationsAttribute attr
     89  *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
     90  * Annotation a = new Annotation("Author", cp);
     91  * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
     92  * attr.setAnnotation(a);
     93  * cf.addAttribute(attr);
     94  * cf.setVersionToJava5();
     95  * </pre></ul>
     96  *
     97  * <p>The last statement is necessary if the class file was produced by
     98  * Javassist or JDK 1.4.  Otherwise, it is not necessary.
     99  *
    100  * @see AnnotationDefaultAttribute
    101  * @see javassist.bytecode.annotation.Annotation
    102  */
    103 public class AnnotationsAttribute extends AttributeInfo {
    104     /**
    105      * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
    106      */
    107     public static final String visibleTag = "RuntimeVisibleAnnotations";
    108 
    109     /**
    110      * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
    111      */
    112     public static final String invisibleTag = "RuntimeInvisibleAnnotations";
    113 
    114     /**
    115      * Constructs a <code>Runtime(In)VisibleAnnotations_attribute</code>.
    116      *
    117      * @param cp            constant pool
    118      * @param attrname      attribute name (<code>visibleTag</code> or
    119      *                      <code>invisibleTag</code>).
    120      * @param info          the contents of this attribute.  It does not
    121      *                      include <code>attribute_name_index</code> or
    122      *                      <code>attribute_length</code>.
    123      */
    124     public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
    125         super(cp, attrname, info);
    126     }
    127 
    128     /**
    129      * Constructs an empty
    130      * <code>Runtime(In)VisibleAnnotations_attribute</code>.
    131      * A new annotation can be later added to the created attribute
    132      * by <code>setAnnotations()</code>.
    133      *
    134      * @param cp            constant pool
    135      * @param attrname      attribute name (<code>visibleTag</code> or
    136      *                      <code>invisibleTag</code>).
    137      * @see #setAnnotations(Annotation[])
    138      */
    139     public AnnotationsAttribute(ConstPool cp, String attrname) {
    140         this(cp, attrname, new byte[] { 0, 0 });
    141     }
    142 
    143     /**
    144      * @param n     the attribute name.
    145      */
    146     AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
    147         throws IOException
    148     {
    149         super(cp, n, in);
    150     }
    151 
    152     /**
    153      * Returns <code>num_annotations</code>.
    154      */
    155     public int numAnnotations() {
    156         return ByteArray.readU16bit(info, 0);
    157     }
    158 
    159     /**
    160      * Copies this attribute and returns a new copy.
    161      */
    162     public AttributeInfo copy(ConstPool newCp, Map classnames) {
    163         Copier copier = new Copier(info, constPool, newCp, classnames);
    164         try {
    165             copier.annotationArray();
    166             return new AnnotationsAttribute(newCp, getName(), copier.close());
    167         }
    168         catch (Exception e) {
    169             throw new RuntimeException(e);
    170         }
    171     }
    172 
    173     /**
    174      * Parses the annotations and returns a data structure representing
    175      * the annotation with the specified type.  See also
    176      * <code>getAnnotations()</code> as to the returned data structure.
    177      *
    178      * @param type      the annotation type.
    179      * @return null if the specified annotation type is not included.
    180      * @see #getAnnotations()
    181      */
    182     public Annotation getAnnotation(String type) {
    183         Annotation[] annotations = getAnnotations();
    184         for (int i = 0; i < annotations.length; i++) {
    185             if (annotations[i].getTypeName().equals(type))
    186                 return annotations[i];
    187         }
    188 
    189         return null;
    190     }
    191 
    192     /**
    193      * Adds an annotation.  If there is an annotation with the same type,
    194      * it is removed before the new annotation is added.
    195      *
    196      * @param annotation        the added annotation.
    197      */
    198     public void addAnnotation(Annotation annotation) {
    199         String type = annotation.getTypeName();
    200         Annotation[] annotations = getAnnotations();
    201         for (int i = 0; i < annotations.length; i++) {
    202             if (annotations[i].getTypeName().equals(type)) {
    203                 annotations[i] = annotation;
    204                 setAnnotations(annotations);
    205                 return;
    206             }
    207         }
    208 
    209         Annotation[] newlist = new Annotation[annotations.length + 1];
    210         System.arraycopy(annotations, 0, newlist, 0, annotations.length);
    211         newlist[annotations.length] = annotation;
    212         setAnnotations(newlist);
    213     }
    214 
    215     /**
    216      * Parses the annotations and returns a data structure representing
    217      * that parsed annotations.  Note that changes of the node values of the
    218      * returned tree are not reflected on the annotations represented by
    219      * this object unless the tree is copied back to this object by
    220      * <code>setAnnotations()</code>.
    221      *
    222      * @see #setAnnotations(Annotation[])
    223      */
    224     public Annotation[] getAnnotations() {
    225         try {
    226             return new Parser(info, constPool).parseAnnotations();
    227         }
    228         catch (Exception e) {
    229             throw new RuntimeException(e);
    230         }
    231     }
    232 
    233     /**
    234      * Changes the annotations represented by this object according to
    235      * the given array of <code>Annotation</code> objects.
    236      *
    237      * @param annotations           the data structure representing the
    238      *                              new annotations.
    239      */
    240     public void setAnnotations(Annotation[] annotations) {
    241         ByteArrayOutputStream output = new ByteArrayOutputStream();
    242         AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
    243         try {
    244             int n = annotations.length;
    245             writer.numAnnotations(n);
    246             for (int i = 0; i < n; ++i)
    247                 annotations[i].write(writer);
    248 
    249             writer.close();
    250         }
    251         catch (IOException e) {
    252             throw new RuntimeException(e);      // should never reach here.
    253         }
    254 
    255         set(output.toByteArray());
    256     }
    257 
    258     /**
    259      * Changes the annotations.  A call to this method is equivalent to:
    260      * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
    261      *
    262      * @param annotation    the data structure representing
    263      *                      the new annotation.
    264      */
    265     public void setAnnotation(Annotation annotation) {
    266         setAnnotations(new Annotation[] { annotation });
    267     }
    268 
    269     /**
    270      * @param oldname       a JVM class name.
    271      * @param newname       a JVM class name.
    272      */
    273     void renameClass(String oldname, String newname) {
    274         HashMap map = new HashMap();
    275         map.put(oldname, newname);
    276         renameClass(map);
    277     }
    278 
    279     void renameClass(Map classnames) {
    280         Renamer renamer = new Renamer(info, getConstPool(), classnames);
    281         try {
    282             renamer.annotationArray();
    283         } catch (Exception e) {
    284             throw new RuntimeException(e);
    285         }
    286     }
    287 
    288     void getRefClasses(Map classnames) { renameClass(classnames); }
    289 
    290     /**
    291      * Returns a string representation of this object.
    292      */
    293     public String toString() {
    294         Annotation[] a = getAnnotations();
    295         StringBuilder sbuf = new StringBuilder();
    296         int i = 0;
    297         while (i < a.length) {
    298             sbuf.append(a[i++].toString());
    299             if (i != a.length)
    300                 sbuf.append(", ");
    301         }
    302 
    303         return sbuf.toString();
    304     }
    305 
    306     static class Walker {
    307         byte[] info;
    308 
    309         Walker(byte[] attrInfo) {
    310             info = attrInfo;
    311         }
    312 
    313         final void parameters() throws Exception {
    314             int numParam = info[0] & 0xff;
    315             parameters(numParam, 1);
    316         }
    317 
    318         void parameters(int numParam, int pos) throws Exception {
    319             for (int i = 0; i < numParam; ++i)
    320                 pos = annotationArray(pos);
    321         }
    322 
    323         final void annotationArray() throws Exception {
    324             annotationArray(0);
    325         }
    326 
    327         final int annotationArray(int pos) throws Exception {
    328             int num = ByteArray.readU16bit(info, pos);
    329             return annotationArray(pos + 2, num);
    330         }
    331 
    332         int annotationArray(int pos, int num) throws Exception {
    333             for (int i = 0; i < num; ++i)
    334                 pos = annotation(pos);
    335 
    336             return pos;
    337         }
    338 
    339         final int annotation(int pos) throws Exception {
    340             int type = ByteArray.readU16bit(info, pos);
    341             int numPairs = ByteArray.readU16bit(info, pos + 2);
    342             return annotation(pos + 4, type, numPairs);
    343         }
    344 
    345         int annotation(int pos, int type, int numPairs) throws Exception {
    346             for (int j = 0; j < numPairs; ++j)
    347                 pos = memberValuePair(pos);
    348 
    349             return pos;
    350         }
    351 
    352         final int memberValuePair(int pos) throws Exception {
    353             int nameIndex = ByteArray.readU16bit(info, pos);
    354             return memberValuePair(pos + 2, nameIndex);
    355         }
    356 
    357         int memberValuePair(int pos, int nameIndex) throws Exception {
    358             return memberValue(pos);
    359         }
    360 
    361         final int memberValue(int pos) throws Exception {
    362             int tag = info[pos] & 0xff;
    363             if (tag == 'e') {
    364                 int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
    365                 int constNameIndex = ByteArray.readU16bit(info, pos + 3);
    366                 enumMemberValue(pos, typeNameIndex, constNameIndex);
    367                 return pos + 5;
    368             }
    369             else if (tag == 'c') {
    370                 int index = ByteArray.readU16bit(info, pos + 1);
    371                 classMemberValue(pos, index);
    372                 return pos + 3;
    373             }
    374             else if (tag == '@')
    375                 return annotationMemberValue(pos + 1);
    376             else if (tag == '[') {
    377                 int num = ByteArray.readU16bit(info, pos + 1);
    378                 return arrayMemberValue(pos + 3, num);
    379             }
    380             else { // primitive types or String.
    381                 int index = ByteArray.readU16bit(info, pos + 1);
    382                 constValueMember(tag, index);
    383                 return pos + 3;
    384             }
    385         }
    386 
    387         void constValueMember(int tag, int index) throws Exception {}
    388 
    389         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
    390             throws Exception {
    391         }
    392 
    393         void classMemberValue(int pos, int index) throws Exception {}
    394 
    395         int annotationMemberValue(int pos) throws Exception {
    396             return annotation(pos);
    397         }
    398 
    399         int arrayMemberValue(int pos, int num) throws Exception {
    400             for (int i = 0; i < num; ++i) {
    401                 pos = memberValue(pos);
    402             }
    403 
    404             return pos;
    405         }
    406     }
    407 
    408     static class Renamer extends Walker {
    409         ConstPool cpool;
    410         Map classnames;
    411 
    412         /**
    413          * Constructs a renamer.  It renames some class names
    414          * into the new names specified by <code>map</code>.
    415          *
    416          * @param info      the annotations attribute.
    417          * @param cp        the constant pool.
    418          * @param map       pairs of replaced and substituted class names.
    419          *                  It can be null.
    420          */
    421         Renamer(byte[] info, ConstPool cp, Map map) {
    422             super(info);
    423             cpool = cp;
    424             classnames = map;
    425         }
    426 
    427         int annotation(int pos, int type, int numPairs) throws Exception {
    428             renameType(pos - 4, type);
    429             return super.annotation(pos, type, numPairs);
    430         }
    431 
    432         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
    433             throws Exception
    434         {
    435             renameType(pos + 1, typeNameIndex);
    436             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
    437         }
    438 
    439         void classMemberValue(int pos, int index) throws Exception {
    440             renameType(pos + 1, index);
    441             super.classMemberValue(pos, index);
    442         }
    443 
    444         private void renameType(int pos, int index) {
    445             String name = cpool.getUtf8Info(index);
    446             String newName = Descriptor.rename(name, classnames);
    447             if (!name.equals(newName)) {
    448                 int index2 = cpool.addUtf8Info(newName);
    449                 ByteArray.write16bit(index2, info, pos);
    450             }
    451         }
    452     }
    453 
    454     static class Copier extends Walker {
    455         ByteArrayOutputStream output;
    456         AnnotationsWriter writer;
    457         ConstPool srcPool, destPool;
    458         Map classnames;
    459 
    460         /**
    461          * Constructs a copier.  This copier renames some class names
    462          * into the new names specified by <code>map</code> when it copies
    463          * an annotation attribute.
    464          *
    465          * @param info      the source attribute.
    466          * @param src       the constant pool of the source class.
    467          * @param dest      the constant pool of the destination class.
    468          * @param map       pairs of replaced and substituted class names.
    469          *                  It can be null.
    470          */
    471         Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
    472             super(info);
    473             output = new ByteArrayOutputStream();
    474             writer = new AnnotationsWriter(output, dest);
    475             srcPool = src;
    476             destPool = dest;
    477             classnames = map;
    478         }
    479 
    480         byte[] close() throws IOException {
    481             writer.close();
    482             return output.toByteArray();
    483         }
    484 
    485         void parameters(int numParam, int pos) throws Exception {
    486             writer.numParameters(numParam);
    487             super.parameters(numParam, pos);
    488         }
    489 
    490         int annotationArray(int pos, int num) throws Exception {
    491             writer.numAnnotations(num);
    492             return super.annotationArray(pos, num);
    493         }
    494 
    495         int annotation(int pos, int type, int numPairs) throws Exception {
    496             writer.annotation(copyType(type), numPairs);
    497             return super.annotation(pos, type, numPairs);
    498         }
    499 
    500         int memberValuePair(int pos, int nameIndex) throws Exception {
    501             writer.memberValuePair(copy(nameIndex));
    502             return super.memberValuePair(pos, nameIndex);
    503         }
    504 
    505         void constValueMember(int tag, int index) throws Exception {
    506             writer.constValueIndex(tag, copy(index));
    507             super.constValueMember(tag, index);
    508         }
    509 
    510         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
    511             throws Exception
    512         {
    513             writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex));
    514             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
    515         }
    516 
    517         void classMemberValue(int pos, int index) throws Exception {
    518             writer.classInfoIndex(copyType(index));
    519             super.classMemberValue(pos, index);
    520         }
    521 
    522         int annotationMemberValue(int pos) throws Exception {
    523             writer.annotationValue();
    524             return super.annotationMemberValue(pos);
    525         }
    526 
    527         int arrayMemberValue(int pos, int num) throws Exception {
    528             writer.arrayValue(num);
    529             return super.arrayMemberValue(pos, num);
    530         }
    531 
    532         /**
    533          * Copies a constant pool entry into the destination constant pool
    534          * and returns the index of the copied entry.
    535          *
    536          * @param srcIndex      the index of the copied entry into the source
    537          *                      constant pool.
    538          * @return the index of the copied item into the destination
    539          *         constant pool.
    540          */
    541         int copy(int srcIndex) {
    542             return srcPool.copy(srcIndex, destPool, classnames);
    543         }
    544 
    545         /**
    546          * Copies a constant pool entry into the destination constant pool
    547          * and returns the index of the copied entry.  That entry must be
    548          * a Utf8Info representing a class name in the L<class name>; form.
    549          *
    550          * @param srcIndex  the index of the copied entry into the source
    551          *                  constant pool.
    552          * @return          the index of the copied item into the destination
    553          *                  constant pool.
    554          */
    555         int copyType(int srcIndex) {
    556             String name = srcPool.getUtf8Info(srcIndex);
    557             String newName = Descriptor.rename(name, classnames);
    558             return destPool.addUtf8Info(newName);
    559         }
    560     }
    561 
    562     static class Parser extends Walker {
    563         ConstPool pool;
    564         Annotation[][] allParams;   // all parameters
    565         Annotation[] allAnno;       // all annotations
    566         Annotation currentAnno;     // current annotation
    567         MemberValue currentMember;  // current member
    568 
    569         /**
    570          * Constructs a parser.  This parser constructs a parse tree of
    571          * the annotations.
    572          *
    573          * @param info      the attribute.
    574          * @param src       the constant pool.
    575          */
    576         Parser(byte[] info, ConstPool cp) {
    577             super(info);
    578             pool = cp;
    579         }
    580 
    581         Annotation[][] parseParameters() throws Exception {
    582             parameters();
    583             return allParams;
    584         }
    585 
    586         Annotation[] parseAnnotations() throws Exception {
    587             annotationArray();
    588             return allAnno;
    589         }
    590 
    591         MemberValue parseMemberValue() throws Exception {
    592             memberValue(0);
    593             return currentMember;
    594         }
    595 
    596         void parameters(int numParam, int pos) throws Exception {
    597             Annotation[][] params = new Annotation[numParam][];
    598             for (int i = 0; i < numParam; ++i) {
    599                 pos = annotationArray(pos);
    600                 params[i] = allAnno;
    601             }
    602 
    603             allParams = params;
    604         }
    605 
    606         int annotationArray(int pos, int num) throws Exception {
    607             Annotation[] array = new Annotation[num];
    608             for (int i = 0; i < num; ++i) {
    609                 pos = annotation(pos);
    610                 array[i] = currentAnno;
    611             }
    612 
    613             allAnno = array;
    614             return pos;
    615         }
    616 
    617         int annotation(int pos, int type, int numPairs) throws Exception {
    618             currentAnno = new Annotation(type, pool);
    619             return super.annotation(pos, type, numPairs);
    620         }
    621 
    622         int memberValuePair(int pos, int nameIndex) throws Exception {
    623             pos = super.memberValuePair(pos, nameIndex);
    624             currentAnno.addMemberValue(nameIndex, currentMember);
    625             return pos;
    626         }
    627 
    628         void constValueMember(int tag, int index) throws Exception {
    629             MemberValue m;
    630             ConstPool cp = pool;
    631             switch (tag) {
    632             case 'B' :
    633                 m = new ByteMemberValue(index, cp);
    634                 break;
    635             case 'C' :
    636                 m = new CharMemberValue(index, cp);
    637                 break;
    638             case 'D' :
    639                 m = new DoubleMemberValue(index, cp);
    640                 break;
    641             case 'F' :
    642                 m = new FloatMemberValue(index, cp);
    643                 break;
    644             case 'I' :
    645                 m = new IntegerMemberValue(index, cp);
    646                 break;
    647             case 'J' :
    648                 m = new LongMemberValue(index, cp);
    649                 break;
    650             case 'S' :
    651                 m = new ShortMemberValue(index, cp);
    652                 break;
    653             case 'Z' :
    654                 m = new BooleanMemberValue(index, cp);
    655                 break;
    656             case 's' :
    657                 m = new StringMemberValue(index, cp);
    658                 break;
    659             default :
    660                 throw new RuntimeException("unknown tag:" + tag);
    661             }
    662 
    663             currentMember = m;
    664             super.constValueMember(tag, index);
    665         }
    666 
    667         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
    668             throws Exception
    669         {
    670             currentMember = new EnumMemberValue(typeNameIndex,
    671                                               constNameIndex, pool);
    672             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
    673         }
    674 
    675         void classMemberValue(int pos, int index) throws Exception {
    676             currentMember = new ClassMemberValue(index, pool);
    677             super.classMemberValue(pos, index);
    678         }
    679 
    680         int annotationMemberValue(int pos) throws Exception {
    681             Annotation anno = currentAnno;
    682             pos = super.annotationMemberValue(pos);
    683             currentMember = new AnnotationMemberValue(currentAnno, pool);
    684             currentAnno = anno;
    685             return pos;
    686         }
    687 
    688         int arrayMemberValue(int pos, int num) throws Exception {
    689             ArrayMemberValue amv = new ArrayMemberValue(pool);
    690             MemberValue[] elements = new MemberValue[num];
    691             for (int i = 0; i < num; ++i) {
    692                 pos = memberValue(pos);
    693                 elements[i] = currentMember;
    694             }
    695 
    696             amv.setValue(elements);
    697             currentMember = amv;
    698             return pos;
    699         }
    700     }
    701 }
    702