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.types.JavaType;
9   import mockit.asm.types.PrimitiveType;
10  import mockit.asm.util.BytecodeReader;
11  
12  import org.checkerframework.checker.index.qual.NonNegative;
13  
14  public final class AnnotationReader extends BytecodeReader {
15      public AnnotationReader(@NonNull BytecodeReader br) {
16          super(br);
17      }
18  
19      /**
20       * Reads the values of a named annotation and makes the given visitor visit them.
21       *
22       * @param startingCodeIndex
23       *            the start offset in {@link #code} of the values to be read (including the unsigned short that gives
24       *            the number of values)
25       * @param av
26       *            the visitor that must visit the values
27       *
28       * @return the end offset of the annotation values
29       */
30      @NonNegative
31      public int readNamedAnnotationValues(@NonNegative int startingCodeIndex, @Nullable AnnotationVisitor av) {
32          codeIndex = startingCodeIndex;
33          readAnnotationValues(true, av);
34          return codeIndex;
35      }
36  
37      private void readAnnotationValues(boolean named, @Nullable AnnotationVisitor av) {
38          int valueCount = readUnsignedShort();
39          readAnnotationValues(valueCount, named, av);
40      }
41  
42      private void readAnnotationValues(@NonNegative int valueCount, boolean named, @Nullable AnnotationVisitor av) {
43          while (valueCount > 0) {
44              String name = named ? readNonnullUTF8() : null;
45              readAnnotationValue(name, av);
46              valueCount--;
47          }
48  
49          if (av != null) {
50              av.visitEnd();
51          }
52      }
53  
54      private void readAnnotationValue(@Nullable String name, @Nullable AnnotationVisitor av) {
55          int typeCode = readUnsignedByte();
56  
57          if (av == null) {
58              readAnnotationValue(typeCode);
59          } else {
60              Object value = readAnnotationValueIfPrimitiveOrString(typeCode);
61  
62              if (value != null) {
63                  av.visit(name, value);
64              } else {
65                  // noinspection SwitchStatementWithoutDefaultBranch
66                  switch (typeCode) {
67                      case 'e':
68                          readEnumConstValue(name, av);
69                          break; // enum_const_value
70                      case 'c':
71                          readClassInfo(name, av);
72                          break; // class_info
73                      case '@':
74                          readNestedAnnotation(name, av);
75                          break; // annotation_value
76                      case '[':
77                          readArrayValue(name, av); // array_value
78                  }
79              }
80          }
81      }
82  
83      private void readAnnotationValue(@NonNegative int typeCode) {
84          switch (typeCode) {
85              case 'e':
86                  codeIndex += 4;
87                  break; // enum_const_value
88              case '@':
89                  codeIndex += 2;
90                  readAnnotationValues(true, null);
91                  break; // annotation_value
92              case '[':
93                  readAnnotationValues(false, null);
94                  break;
95              default:
96                  codeIndex += 2;
97          }
98      }
99  
100     @Nullable
101     @SuppressWarnings({ "NumericCastThatLosesPrecision", "SwitchStatementWithoutDefaultBranch" })
102     private Object readAnnotationValueIfPrimitiveOrString(@NonNegative int typeCode) {
103         switch (typeCode) {
104             case 'I':
105             case 'J':
106             case 'F':
107             case 'D':
108                 return readConstItem(); // CONSTANT_Integer/Long/Float/Double
109             case 'B':
110                 return (byte) readValueOfOneOrTwoBytes(); // CONSTANT_Byte
111             case 'Z':
112                 return readValueOfOneOrTwoBytes() != 0; // CONSTANT_Boolean
113             case 'S':
114                 return (short) readValueOfOneOrTwoBytes(); // CONSTANT_Short
115             case 'C':
116                 return (char) readValueOfOneOrTwoBytes(); // CONSTANT_Char
117             case 's':
118                 return readNonnullUTF8(); // CONSTANT_Utf8
119         }
120 
121         return null;
122     }
123 
124     private int readValueOfOneOrTwoBytes() {
125         int itemIndex = readUnsignedShort();
126         int valueCodeIndex = items[itemIndex];
127         return readInt(valueCodeIndex);
128     }
129 
130     private void readEnumConstValue(@Nullable String name, @NonNull AnnotationVisitor av) {
131         String enumDesc = readNonnullUTF8();
132         String enumValue = readNonnullUTF8();
133         av.visitEnum(name, enumDesc, enumValue);
134     }
135 
136     private void readClassInfo(@Nullable String name, @NonNull AnnotationVisitor av) {
137         String typeDesc = readNonnullUTF8();
138         JavaType value = JavaType.getType(typeDesc);
139         av.visit(name, value);
140     }
141 
142     private void readNestedAnnotation(@Nullable String name, @NonNull AnnotationVisitor av) {
143         String desc = readNonnullUTF8();
144         AnnotationVisitor nestedVisitor = av.visitAnnotation(name, desc);
145         readAnnotationValues(true, nestedVisitor);
146     }
147 
148     private void readArrayValue(@Nullable String name, @NonNull AnnotationVisitor av) {
149         int valueCount = readUnsignedShort();
150 
151         if (valueCount == 0) {
152             AnnotationVisitor arrayVisitor = av.visitArray(name);
153             arrayVisitor.visitEnd();
154             return;
155         }
156 
157         int typeCode = readUnsignedByte();
158         PrimitiveType primitiveElementType = PrimitiveType.getPrimitiveType(typeCode);
159 
160         if (primitiveElementType == null) {
161             AnnotationVisitor arrayVisitor = av.visitArray(name);
162             codeIndex--;
163             readAnnotationValues(valueCount, false, arrayVisitor);
164             return;
165         }
166 
167         Class<?> elementType = primitiveElementType.getType();
168         Object array = Array.newInstance(elementType, valueCount);
169         fillArrayElements(valueCount, typeCode, array);
170         av.visit(name, array);
171         codeIndex--;
172     }
173 
174     private void fillArrayElements(@NonNegative int length, @NonNegative int typeCode, @NonNull Object array) {
175         for (int i = 0; i < length; i++) {
176             int itemIndex = readUnsignedShort();
177             int index = items[itemIndex];
178             Object value = getArrayElementValue(typeCode, index);
179             Array.set(array, i, value);
180             codeIndex++;
181         }
182     }
183 
184     @NonNull
185     private Object getArrayElementValue(@NonNegative int typeCode, @NonNegative int valueCodeIndex) {
186         switch (typeCode) {
187             case 'Z':
188                 return readBoolean(valueCodeIndex);
189             case 'C':
190                 return readChar(valueCodeIndex);
191             case 'B':
192                 return readUnsignedByte(valueCodeIndex);
193             case 'S':
194                 return readShort(valueCodeIndex);
195             case 'F':
196                 return readFloat(valueCodeIndex);
197             case 'D':
198                 return readDouble(valueCodeIndex);
199             case 'J':
200                 return readLong(valueCodeIndex);
201             default:
202                 return readInt(valueCodeIndex);
203         }
204     }
205 }