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