1 package mockit.asm.constantPool;
2
3 import static mockit.asm.jvmConstants.ConstantPoolTypes.CLASS;
4 import static mockit.asm.jvmConstants.ConstantPoolTypes.DOUBLE;
5 import static mockit.asm.jvmConstants.ConstantPoolTypes.FIELD_REF;
6 import static mockit.asm.jvmConstants.ConstantPoolTypes.FLOAT;
7 import static mockit.asm.jvmConstants.ConstantPoolTypes.IMETHOD_REF;
8 import static mockit.asm.jvmConstants.ConstantPoolTypes.INTEGER;
9 import static mockit.asm.jvmConstants.ConstantPoolTypes.LONG;
10 import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_HANDLE;
11 import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_REF;
12 import static mockit.asm.jvmConstants.ConstantPoolTypes.METHOD_TYPE;
13 import static mockit.asm.jvmConstants.ConstantPoolTypes.NAME_TYPE;
14 import static mockit.asm.jvmConstants.ConstantPoolTypes.STRING;
15 import static mockit.asm.jvmConstants.ConstantPoolTypes.UTF8;
16 import static mockit.internal.util.ClassLoad.OBJECT;
17
18 import edu.umd.cs.findbugs.annotations.NonNull;
19 import edu.umd.cs.findbugs.annotations.Nullable;
20
21 import mockit.asm.jvmConstants.ConstantPoolTypes;
22 import mockit.asm.types.JavaType;
23 import mockit.asm.types.MethodType;
24 import mockit.asm.types.PrimitiveType;
25 import mockit.asm.types.ReferenceType;
26 import mockit.asm.util.ByteVector;
27 import mockit.asm.util.MethodHandle;
28 import mockit.internal.util.ClassLoad;
29
30 import org.checkerframework.checker.index.qual.NonNegative;
31
32
33
34
35
36 @SuppressWarnings({ "ClassWithTooManyFields", "OverlyCoupledClass" })
37 public final class ConstantPoolGeneration {
38
39
40
41 @NonNull
42 private final ByteVector pool;
43
44
45
46
47 @NonNull
48 private Item[] items;
49
50
51
52
53 @NonNegative
54 private int threshold;
55
56
57
58
59 @NonNegative
60 private int index;
61
62 @NonNull
63 private final StringItem reusableUTF8Item;
64 @NonNull
65 private final StringItem reusableStringItem;
66 @NonNull
67 private final NameAndTypeItem reusableNameTypeItem;
68 @NonNull
69 private final ClassMemberItem reusableClassMemberItem;
70 @NonNull
71 private final IntItem reusableIntItem;
72 @NonNull
73 private final LongItem reusableLongItem;
74 @NonNull
75 private final FloatItem reusableFloatItem;
76 @NonNull
77 private final DoubleItem reusableDoubleItem;
78 @NonNull
79 private final MethodHandleItem reusableMethodHandleItem;
80 @NonNull
81 private final DynamicItem reusableDynamicItem;
82
83
84
85
86
87
88
89
90
91
92 private TypeTableItem[] typeTable;
93
94
95
96
97 private short typeCount;
98
99 @NonNull
100 private final NormalTypeTableItem reusableNormalItem;
101 @NonNull
102 private final UninitializedTypeTableItem reusableUninitializedItem;
103 @NonNull
104 private final MergedTypeTableItem reusableMergedItem;
105
106 @SuppressWarnings("OverlyCoupledMethod")
107 public ConstantPoolGeneration() {
108 pool = new ByteVector();
109 items = new Item[256];
110
111 threshold = (int) (0.75d * items.length);
112 index = 1;
113 reusableUTF8Item = new StringItem();
114 reusableStringItem = new StringItem();
115 reusableNameTypeItem = new NameAndTypeItem(0);
116 reusableClassMemberItem = new ClassMemberItem(0);
117 reusableIntItem = new IntItem(0);
118 reusableLongItem = new LongItem(0);
119 reusableFloatItem = new FloatItem(0);
120 reusableDoubleItem = new DoubleItem(0);
121 reusableMethodHandleItem = new MethodHandleItem(0);
122 reusableDynamicItem = new DynamicItem(0);
123 reusableNormalItem = new NormalTypeTableItem();
124 reusableUninitializedItem = new UninitializedTypeTableItem();
125 reusableMergedItem = new MergedTypeTableItem();
126 }
127
128
129
130
131
132
133
134
135
136
137 @NonNegative
138 public int newUTF8(@NonNull String value) {
139 reusableUTF8Item.set(UTF8, value);
140
141 StringItem result = get(reusableUTF8Item);
142
143 if (result == null) {
144 pool.putByte(UTF8).putUTF8(value);
145
146 result = new StringItem(index++, reusableUTF8Item);
147 put(result);
148 }
149
150 return result.index;
151 }
152
153
154
155
156
157
158
159
160
161
162 @NonNegative
163 public int newClass(@NonNull String internalName) {
164 return newClassItem(internalName).index;
165 }
166
167
168
169
170
171
172
173
174
175
176 @NonNull
177 public StringItem newClassItem(@NonNull String internalName) {
178 return newStringItem(CLASS, internalName);
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192
193 @NonNull
194 private StringItem newStringItem(int type, @NonNull String value) {
195 reusableStringItem.set(type, value);
196
197 StringItem result = get(reusableStringItem);
198
199 if (result == null) {
200 int itemIndex = newUTF8(value);
201 pool.put12(type, itemIndex);
202
203 result = new StringItem(index++, reusableStringItem);
204 put(result);
205 }
206
207 return result;
208 }
209
210
211
212
213
214
215
216 @NonNull
217 public MethodHandleItem newMethodHandleItem(@NonNull MethodHandle methodHandle) {
218 reusableMethodHandleItem.set(methodHandle);
219
220 MethodHandleItem result = get(reusableMethodHandleItem);
221
222 if (result == null) {
223 int tag = methodHandle.tag;
224 int memberType = tag == MethodHandle.Tag.TAG_INVOKEINTERFACE ? IMETHOD_REF : METHOD_REF;
225 ClassMemberItem memberItem = newClassMemberItem(memberType, methodHandle.owner, methodHandle.name,
226 methodHandle.desc);
227 pool.put11(METHOD_HANDLE, tag).putShort(memberItem.index);
228
229 result = new MethodHandleItem(index++, reusableMethodHandleItem);
230 put(result);
231 }
232
233 return result;
234 }
235
236 @NonNull
237 private ClassMemberItem newClassMemberItem(int type, @NonNull String owner, @NonNull String name,
238 @NonNull String desc) {
239 reusableClassMemberItem.set(type, owner, name, desc);
240
241 ClassMemberItem result = get(reusableClassMemberItem);
242
243 if (result == null) {
244 int ownerItemIndex = newClass(owner);
245 int nameAndTypeItemIndex = newNameType(name, desc);
246 put122(type, ownerItemIndex, nameAndTypeItemIndex);
247
248 result = new ClassMemberItem(index++, reusableClassMemberItem);
249 put(result);
250 }
251
252 return result;
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 @NonNull
269 public ClassMemberItem newFieldItem(@NonNull String owner, @NonNull String name, @NonNull String desc) {
270 return newClassMemberItem(FIELD_REF, owner, name, desc);
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288 @NonNull
289 public ClassMemberItem newMethodItem(@NonNull String owner, @NonNull String name, @NonNull String desc,
290 boolean itf) {
291 return newClassMemberItem(itf ? IMETHOD_REF : METHOD_REF, owner, name, desc);
292 }
293
294
295
296
297
298
299
300
301
302
303 @NonNull
304 public IntItem newInteger(int value) {
305 reusableIntItem.setValue(value);
306
307 IntItem result = get(reusableIntItem);
308
309 if (result == null) {
310 pool.putByte(INTEGER).putInt(value);
311
312 result = new IntItem(index++, reusableIntItem);
313 put(result);
314 }
315
316 return result;
317 }
318
319
320
321
322
323
324
325
326
327
328 @NonNull
329 public FloatItem newFloat(float value) {
330 reusableFloatItem.set(value);
331
332 FloatItem result = get(reusableFloatItem);
333
334 if (result == null) {
335 pool.putByte(FLOAT).putInt(reusableFloatItem.intVal);
336
337 result = new FloatItem(index++, reusableFloatItem);
338 put(result);
339 }
340
341 return result;
342 }
343
344
345
346
347
348
349
350
351
352
353 @NonNull
354 public LongItem newLong(long value) {
355 reusableLongItem.setValue(value);
356
357 LongItem result = get(reusableLongItem);
358
359 if (result == null) {
360 pool.putByte(LONG).putLong(value);
361
362 result = new LongItem(index, reusableLongItem);
363 index += 2;
364 put(result);
365 }
366
367 return result;
368 }
369
370
371
372
373
374
375
376
377
378
379 @NonNull
380 public DoubleItem newDouble(double value) {
381 reusableDoubleItem.set(value);
382
383 DoubleItem result = get(reusableDoubleItem);
384
385 if (result == null) {
386 pool.putByte(DOUBLE).putLong(reusableDoubleItem.longVal);
387
388 result = new DoubleItem(index, reusableDoubleItem);
389 index += 2;
390 put(result);
391 }
392
393 return result;
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407 @NonNegative
408 private int newNameType(@NonNull String name, @NonNull String desc) {
409 reusableNameTypeItem.set(name, desc);
410
411 NameAndTypeItem result = get(reusableNameTypeItem);
412
413 if (result == null) {
414 int nameItemIndex = newUTF8(name);
415 int descItemIndex = newUTF8(desc);
416 put122(NAME_TYPE, nameItemIndex, descItemIndex);
417
418 result = new NameAndTypeItem(index++, reusableNameTypeItem);
419 put(result);
420 }
421
422 return result.index;
423 }
424
425
426
427
428
429
430
431
432
433
434
435 @NonNull
436 public Item newConstItem(@NonNull Object cst) {
437 if (cst instanceof String) {
438 return newStringItem(STRING, (String) cst);
439 }
440
441 if (cst instanceof Number) {
442 return newNumberItem((Number) cst);
443 }
444
445 if (cst instanceof Character) {
446 return newInteger((Character) cst);
447 }
448
449 if (cst instanceof Boolean) {
450 int val = (Boolean) cst ? 1 : 0;
451 return newInteger(val);
452 }
453
454 if (cst instanceof ReferenceType) {
455 String typeDesc = ((ReferenceType) cst).getInternalName();
456 return cst instanceof MethodType ? newStringItem(METHOD_TYPE, typeDesc) : newClassItem(typeDesc);
457 }
458
459 if (cst instanceof PrimitiveType) {
460 String typeDesc = ((PrimitiveType) cst).getDescriptor();
461 return newClassItem(typeDesc);
462 }
463
464 if (cst instanceof MethodHandle) {
465 return newMethodHandleItem((MethodHandle) cst);
466 }
467
468 if (cst instanceof DynamicItem) {
469 DynamicItem dynamicItem = (DynamicItem) cst;
470 return createDynamicItem(dynamicItem.type, dynamicItem.name, dynamicItem.desc, dynamicItem.bsmIndex);
471 }
472 throw new IllegalArgumentException("value " + cst);
473 }
474
475 @NonNull
476 private Item newNumberItem(@NonNull Number cst) {
477 if (cst instanceof Float) {
478 return newFloat(cst.floatValue());
479 }
480
481 if (cst instanceof Long) {
482 return newLong(cst.longValue());
483 }
484
485 if (cst instanceof Double) {
486 return newDouble(cst.doubleValue());
487 }
488
489 return newInteger(cst.intValue());
490 }
491
492
493
494
495
496
497
498
499
500
501 @NonNegative
502 public int addNormalType(@NonNull String type) {
503 reusableNormalItem.set(type);
504
505 TypeTableItem result = get(reusableNormalItem);
506
507 if (result == null) {
508 result = new NormalTypeTableItem(++typeCount, reusableNormalItem);
509 addToTypeTable(result);
510 }
511
512 return result.index;
513 }
514
515
516
517
518
519
520
521
522
523
524
525
526 @NonNegative
527 public int addUninitializedType(@NonNull String type, @NonNegative int offset) {
528 reusableUninitializedItem.set(type, offset);
529
530 TypeTableItem result = get(reusableUninitializedItem);
531
532 if (result == null) {
533 result = new UninitializedTypeTableItem(++typeCount, reusableUninitializedItem);
534 addToTypeTable(result);
535 }
536
537 return result.index;
538 }
539
540 private void addToTypeTable(@NonNull TypeTableItem newItem) {
541 put(newItem);
542
543 if (typeTable == null) {
544 typeTable = new TypeTableItem[16];
545 }
546
547 int newItemIndex = typeCount;
548 enlargeTypeTableIfNeeded(newItemIndex);
549 typeTable[newItemIndex] = newItem;
550 }
551
552 private void enlargeTypeTableIfNeeded(@NonNegative int newItemIndex) {
553 int currentTypeCount = typeTable.length;
554
555 if (newItemIndex == currentTypeCount) {
556 TypeTableItem[] newTable = new TypeTableItem[2 * currentTypeCount];
557 System.arraycopy(typeTable, 0, newTable, 0, currentTypeCount);
558 typeTable = newTable;
559 }
560 }
561
562
563
564
565
566
567
568
569
570
571
572
573 @NonNegative
574 public int getMergedType(@NonNegative int type1, @NonNegative int type2) {
575 reusableMergedItem.set(type1, type2);
576
577 MergedTypeTableItem result = get(reusableMergedItem);
578
579 if (result == null) {
580 String type1Desc = getInternalName(type1);
581 String type2Desc = getInternalName(type2);
582 String commonSuperClass = getCommonSuperClass(type1Desc, type2Desc);
583 reusableMergedItem.commonSuperTypeIndex = addNormalType(commonSuperClass);
584
585 result = new MergedTypeTableItem(reusableMergedItem);
586 put(result);
587 }
588
589 return result.commonSuperTypeIndex;
590 }
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 @NonNull
607 private static String getCommonSuperClass(@NonNull String type1, @NonNull String type2) {
608
609 String class1 = type1;
610 String class2 = type2;
611
612 while (true) {
613 if (OBJECT.equals(class1) || OBJECT.equals(class2)) {
614 return OBJECT;
615 }
616
617 String superClass = ClassLoad.whichIsSuperClass(class1, class2);
618
619 if (superClass != null) {
620 return superClass;
621 }
622
623 class1 = ClassLoad.getSuperClass(class1);
624 class2 = ClassLoad.getSuperClass(class2);
625
626 if (class1.equals(class2)) {
627 return class1;
628 }
629 }
630 }
631
632 @NonNull
633 public String getInternalName(@NonNegative int typeTableIndex) {
634 TypeTableItem typeTableItem = typeTable[typeTableIndex];
635 return typeTableItem.typeDesc;
636 }
637
638 @NonNull
639 public UninitializedTypeTableItem getUninitializedItemValue(@NonNegative int typeTableIndex) {
640 return (UninitializedTypeTableItem) typeTable[typeTableIndex];
641 }
642
643 @Nullable
644 public Item getItem(@NonNegative int itemHashCode) {
645 return items[itemHashCode % items.length];
646 }
647
648
649
650
651
652
653
654
655
656
657 @Nullable
658 private <I extends Item> I get(@NonNull I key) {
659 Item item = getItem(key.getHashCode());
660 int keyType = key.getType();
661
662 while (item != null && (item.getType() != keyType || !key.isEqualTo(item))) {
663 item = item.getNext();
664 }
665
666
667 return (I) item;
668 }
669
670
671
672
673
674
675
676 private void put(@NonNull Item item) {
677 resizeItemArrayIfNeeded();
678 item.setNext(items);
679 }
680
681 private void resizeItemArrayIfNeeded() {
682 if (index + typeCount > threshold) {
683 int ll = items.length;
684 int nl = ll * 2 + 1;
685 Item[] newItems = new Item[nl];
686
687 for (int l = ll - 1; l >= 0; l--) {
688 Item j = items[l];
689 put(newItems, j);
690 }
691
692 items = newItems;
693
694 threshold = (int) (nl * 0.75);
695 }
696 }
697
698 private static void put(@NonNull Item[] newItems, @Nullable Item item) {
699 while (item != null) {
700 Item next = item.getNext();
701 item.setNext(newItems);
702
703 item = next;
704 }
705 }
706
707
708
709
710 private void put122(int b, int s1, int s2) {
711 pool.put12(b, s1).putShort(s2);
712 }
713
714 @NonNegative
715 public int getSize() {
716 return pool.getLength();
717 }
718
719 public void checkConstantPoolMaxSize() {
720 if (index > 0xFFFF) {
721 throw new RuntimeException("Class file too large!");
722 }
723 }
724
725 public void put(@NonNull ByteVector out) {
726 out.putShort(index).putByteVector(pool);
727 }
728
729 public void copy(@NonNull byte[] code, @NonNegative int off, @NonNegative int header, @NonNull Item[] cpItems) {
730 pool.putByteArray(code, off, header - off);
731 items = cpItems;
732
733 int ll = cpItems.length;
734
735 threshold = (int) (0.75d * ll);
736 index = ll;
737 }
738
739 @NonNull
740 public DynamicItem createDynamicItem(int type, @NonNull String name, @NonNull String desc,
741 @NonNegative int bsmIndex) {
742 reusableDynamicItem.set(type, name, desc, bsmIndex);
743
744 DynamicItem result = get(reusableDynamicItem);
745
746 if (result == null) {
747 int nameAndTypeItemIndex = newNameType(name, desc);
748 put122(type, bsmIndex, nameAndTypeItemIndex);
749
750 result = new DynamicItem(index++, reusableDynamicItem);
751 put(result);
752 }
753
754 return result;
755 }
756 }