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