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
21
22
23
24
25
26
27
28
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
66 switch (typeCode) {
67 case 'e':
68 readEnumConstValue(name, av);
69 break;
70 case 'c':
71 readClassInfo(name, av);
72 break;
73 case '@':
74 readNestedAnnotation(name, av);
75 break;
76 case '[':
77 readArrayValue(name, av);
78 }
79 }
80 }
81 }
82
83 private void readAnnotationValue(@NonNegative int typeCode) {
84 switch (typeCode) {
85 case 'e':
86 codeIndex += 4;
87 break;
88 case '@':
89 codeIndex += 2;
90 readAnnotationValues(true, null);
91 break;
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();
109 case 'B':
110 return (byte) readValueOfOneOrTwoBytes();
111 case 'Z':
112 return readValueOfOneOrTwoBytes() != 0;
113 case 'S':
114 return (short) readValueOfOneOrTwoBytes();
115 case 'C':
116 return (char) readValueOfOneOrTwoBytes();
117 case 's':
118 return readNonnullUTF8();
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 }