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