View Javadoc
1   package mockit.asm.annotations;
2   
3   import edu.umd.cs.findbugs.annotations.NonNull;
4   import edu.umd.cs.findbugs.annotations.Nullable;
5   
6   import java.lang.reflect.Array;
7   
8   import mockit.asm.constantPool.ConstantPoolGeneration;
9   import mockit.asm.constantPool.Item;
10  import mockit.asm.types.JavaType;
11  import mockit.asm.util.ByteVector;
12  
13  import org.checkerframework.checker.index.qual.NonNegative;
14  
15  /**
16   * A visitor to visit a Java annotation, in the following order: (<code>visit</code> | <code>visitEnum</code> |
17   * <code>visitAnnotation</code> | <code>visitArray</code>)* <code>visitEnd</code>.
18   */
19  public final class AnnotationVisitor {
20      /**
21       * The constant pool to which this annotation must be added.
22       */
23      @NonNull
24      private final ConstantPoolGeneration cp;
25  
26      /**
27       * The number of attribute values in this annotation.
28       */
29      @NonNegative
30      private int attributeCount;
31  
32      /**
33       * <code>true<code> if values are named, <code>false</code> otherwise. Annotation writers used for annotation
34       * default and annotation arrays use unnamed values.
35       */
36      private final boolean named;
37  
38      /**
39       * The annotation values in bytecode form. This byte vector only contains the values themselves, i.e. the number of
40       * values must be stored as an unsigned short just before these bytes.
41       */
42      @NonNull
43      private final ByteVector bv;
44  
45      /**
46       * Where the number of values of this annotation must be stored in {@link #bv}.
47       */
48      @NonNegative
49      private final int offset;
50  
51      /**
52       * Next annotation visitor. This field is used to store annotation lists.
53       */
54      @Nullable
55      private AnnotationVisitor next;
56  
57      /**
58       * Previous annotation visitor. This field is used to store annotation lists.
59       */
60      @Nullable
61      private AnnotationVisitor prev;
62  
63      public AnnotationVisitor(@NonNull ConstantPoolGeneration cp, @NonNull String typeDesc) {
64          this.cp = cp;
65          named = true;
66          bv = new ByteVector();
67          bv.putShort(cp.newUTF8(typeDesc));
68          bv.putShort(0); // reserve space for value count
69          offset = 2;
70      }
71  
72      private AnnotationVisitor(@NonNull AnnotationVisitor parent, boolean named) {
73          cp = parent.cp;
74          this.named = named;
75          bv = parent.bv;
76          offset = getByteLength() - 2;
77      }
78  
79      @NonNegative
80      private int getByteLength() {
81          return bv.getLength();
82      }
83  
84      /**
85       * Sets the visitor to the {@link #next} annotation.
86       */
87      public void setNext(@Nullable AnnotationVisitor next) {
88          this.next = next;
89      }
90  
91      /**
92       * Visits a primitive, String, Class, or array value of the annotation.
93       *
94       * @param name
95       *            the value name
96       * @param value
97       *            the actual value, whose type must be {@link Byte}, {@link Boolean}, {@link Character}, {@link Short},
98       *            {@link Integer}, {@link Long}, {@link Float}, {@link Double}, {@link String}, or {@link JavaType} of
99       *            OBJECT or ARRAY sort; this value can also be an array of byte, boolean, short, char, int, long, float
100      *            or double values (this is equivalent to using {@link #visitArray} and visiting each array element in
101      *            turn, but is more convenient)
102      */
103     void visit(@Nullable String name, @NonNull Object value) {
104         putName(name);
105 
106         if (value instanceof String) {
107             putString('s', (String) value);
108         } else if (putValueWhenPrimitive(value)) {
109             // OK
110         } else if (value instanceof JavaType) {
111             putType((JavaType) value);
112         } else {
113             putElementValuesWhenArray(value);
114         }
115     }
116 
117     private void putName(@Nullable String name) {
118         attributeCount++;
119 
120         if (named) {
121             // noinspection ConstantConditions
122             putString(name);
123         }
124     }
125 
126     private boolean putValueWhenPrimitive(@NonNull Object value) {
127         if (value instanceof Boolean) {
128             putBoolean((Boolean) value);
129         } else if (value instanceof Integer) {
130             putInteger('I', (Integer) value);
131         } else if (value instanceof Double) {
132             putDouble((Double) value);
133         } else if (value instanceof Float) {
134             putFloat((Float) value);
135         } else if (value instanceof Long) {
136             putLong((Long) value);
137         } else if (value instanceof Byte) {
138             putInteger('B', (Byte) value);
139         } else if (value instanceof Character) {
140             putInteger('C', (Character) value);
141         } else if (value instanceof Short) {
142             putInteger('S', (Short) value);
143         } else {
144             return false;
145         }
146 
147         return true;
148     }
149 
150     private void putItem(int typeCode, @NonNull Item item) {
151         bv.put12(typeCode, item.index);
152     }
153 
154     private void putBoolean(boolean value) {
155         putInteger('Z', value ? 1 : 0);
156     }
157 
158     private void putInteger(int typeCode, int value) {
159         Item item = cp.newInteger(value);
160         putItem(typeCode, item);
161     }
162 
163     private void putDouble(double value) {
164         Item item = cp.newDouble(value);
165         putItem('D', item);
166     }
167 
168     private void putFloat(float value) {
169         Item item = cp.newFloat(value);
170         putItem('F', item);
171     }
172 
173     private void putLong(long value) {
174         Item item = cp.newLong(value);
175         putItem('J', item);
176     }
177 
178     private void putType(@NonNull JavaType type) {
179         String typeDescriptor = type.getDescriptor();
180         putString('c', typeDescriptor);
181     }
182 
183     private void putString(int b, @NonNull String value) {
184         int itemIndex = cp.newUTF8(value);
185         bv.put12(b, itemIndex);
186     }
187 
188     private void putString(@NonNull String value) {
189         int itemIndex = cp.newUTF8(value);
190         bv.putShort(itemIndex);
191     }
192 
193     private void putArrayLength(@NonNegative int length) {
194         bv.put12('[', length);
195     }
196 
197     private void putElementValuesWhenArray(@NonNull Object value) {
198         if (value instanceof byte[]) {
199             putArrayElementValues('B', value);
200         } else if (value instanceof boolean[]) {
201             putArrayElementValues('Z', value);
202         } else if (value instanceof short[]) {
203             putArrayElementValues('S', value);
204         } else if (value instanceof char[]) {
205             putArrayElementValues('C', value);
206         } else if (value instanceof int[]) {
207             putArrayElementValues('I', value);
208         } else if (value instanceof long[]) {
209             putArrayElementValues('J', value);
210         } else if (value instanceof float[]) {
211             putArrayElementValues('F', value);
212         } else if (value instanceof double[]) {
213             putArrayElementValues('D', value);
214         }
215     }
216 
217     private void putArrayElementValues(char elementType, @NonNull Object array) {
218         int length = Array.getLength(array);
219         putArrayLength(length);
220 
221         for (int i = 0; i < length; i++) {
222             switch (elementType) {
223                 case 'J': {
224                     long value = Array.getLong(array, i);
225                     putLong(value);
226                     break;
227                 }
228                 case 'F': {
229                     float value = Array.getFloat(array, i);
230                     putFloat(value);
231                     break;
232                 }
233                 case 'D': {
234                     double value = Array.getDouble(array, i);
235                     putDouble(value);
236                     break;
237                 }
238                 case 'Z': {
239                     boolean value = Array.getBoolean(array, i);
240                     putBoolean(value);
241                     break;
242                 }
243                 default: {
244                     int value = Array.getInt(array, i);
245                     putInteger(elementType, value);
246                     break;
247                 }
248             }
249         }
250     }
251 
252     /**
253      * Visits an enumeration value of the annotation.
254      *
255      * @param name
256      *            the value name
257      * @param desc
258      *            the class descriptor of the enumeration class
259      * @param value
260      *            the actual enumeration value
261      */
262     void visitEnum(@Nullable String name, @NonNull String desc, @NonNull String value) {
263         putName(name);
264         putString('e', desc);
265         putString(value);
266     }
267 
268     /**
269      * Visits a nested annotation value of the annotation.
270      *
271      * @param name
272      *            the value name
273      * @param desc
274      *            the class descriptor of the nested annotation class
275      *
276      * @return a visitor to visit the actual nested annotation value
277      */
278     @NonNull
279     AnnotationVisitor visitAnnotation(@Nullable String name, @NonNull String desc) {
280         putName(name);
281 
282         // Write tag and type, and reserve space for value count.
283         putString('@', desc);
284         bv.putShort(0);
285 
286         return new AnnotationVisitor(this, true);
287     }
288 
289     /**
290      * Visits an array value of the annotation. Note that arrays of primitive types can be passed as value to
291      * {@link #visit(String, Object)}.
292      *
293      * @param name
294      *            the value name
295      *
296      * @return a visitor to visit the actual array value elements; the 'name' parameters passed to the methods of this
297      *         visitor are ignored
298      */
299     @NonNull
300     AnnotationVisitor visitArray(@Nullable String name) {
301         putName(name);
302 
303         // Write tag, and reserve space for array size.
304         putArrayLength(0);
305 
306         return new AnnotationVisitor(this, false);
307     }
308 
309     /**
310      * Visits the end of the annotation.
311      */
312     @SuppressWarnings("NumericCastThatLosesPrecision")
313     void visitEnd() {
314         byte[] data = bv.getData();
315         data[offset] = (byte) (attributeCount >>> 8);
316         data[offset + 1] = (byte) attributeCount;
317     }
318 
319     /**
320      * Returns the size of this annotation list.
321      */
322     @NonNegative
323     public int getSize() {
324         int size = 0;
325         AnnotationVisitor annotation = this;
326 
327         while (annotation != null) {
328             size += annotation.getByteLength();
329             annotation = annotation.next;
330         }
331 
332         return size;
333     }
334 
335     /**
336      * Puts the annotations of this annotation writer list into the given byte vector.
337      */
338     public void put(@NonNull ByteVector out) {
339         AnnotationVisitor aw = this;
340         AnnotationVisitor last = null;
341         int n = 0;
342         int size = 2;
343 
344         while (aw != null) {
345             n++;
346             size += aw.getByteLength();
347             aw.prev = last;
348             last = aw;
349             aw = aw.next;
350         }
351 
352         out.putInt(size);
353         out.putShort(n);
354         putFromLastToFirst(out, last);
355     }
356 
357     private static void putFromLastToFirst(@NonNull ByteVector out, @Nullable AnnotationVisitor aw) {
358         while (aw != null) {
359             out.putByteVector(aw.bv);
360             aw = aw.prev;
361         }
362     }
363 
364     /**
365      * Puts the given annotation lists into the given byte vector.
366      */
367     public static void put(@NonNull ByteVector out, @NonNull AnnotationVisitor[] anns) {
368         putNumberAndSizeOfAnnotations(out, anns);
369 
370         for (AnnotationVisitor ann : anns) {
371             AnnotationVisitor last = putNumberOfAnnotations(out, ann);
372             putFromLastToFirst(out, last);
373         }
374     }
375 
376     private static void putNumberAndSizeOfAnnotations(@NonNull ByteVector out, @NonNull AnnotationVisitor[] anns) {
377         int numAnns = anns.length;
378         int size = 1 + 2 * numAnns;
379 
380         for (AnnotationVisitor aw : anns) {
381             if (aw != null) {
382                 size += aw.getSize();
383             }
384         }
385 
386         out.putInt(size).putByte(numAnns);
387     }
388 
389     @Nullable
390     private static AnnotationVisitor putNumberOfAnnotations(@NonNull ByteVector out, @Nullable AnnotationVisitor aw) {
391         AnnotationVisitor last = null;
392         int n = 0;
393 
394         while (aw != null) {
395             n++;
396             aw.prev = last;
397             last = aw;
398             aw = aw.next;
399         }
400 
401         out.putShort(n);
402         return last;
403     }
404 }