View Javadoc
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   * Allows the constant pool for a classfile to be created from scratch, when that classfile itself is being generated or
34   * modified from an existing class file.
35   */
36  @SuppressWarnings({ "ClassWithTooManyFields", "OverlyCoupledClass" })
37  public final class ConstantPoolGeneration {
38      /**
39       * The constant pool of the class file being generated/modified.
40       */
41      @NonNull
42      private final ByteVector pool;
43  
44      /**
45       * The constant pool's hash table data.
46       */
47      @NonNull
48      private Item[] items;
49  
50      /**
51       * The threshold of the constant pool's hash table.
52       */
53      @NonNegative
54      private int threshold;
55  
56      /**
57       * Index of the next item to be added in the constant pool.
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       * A type table used to temporarily store internal names that will not necessarily be stored in the constant pool.
85       * This type table is used by the control flow and data flow analysis algorithm to compute stack map frames from
86       * scratch. This array associates to each index <code>i</code> the <code>TypeTableItem</code> whose index is
87       * <code>i</code>. All <code>TypeTableItem</code> objects stored in this array are also stored in the {@link #items}
88       * hash table. These two arrays allow to retrieve an <code>Item</code> from its index or, conversely, to get the
89       * index of an <code>Item</code> from its value. Each <code>TypeTableItem</code> stores an internal name in its
90       * {@link TypeTableItem#typeDesc} field.
91       */
92      private TypeTableItem[] typeTable;
93  
94      /**
95       * Number of elements in the {@link #typeTable} array.
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         // noinspection NumericCastThatLosesPrecision
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      * Adds an UTF8 string to the constant pool of the class being built. Does nothing if the constant pool already
130      * contains a similar item.
131      *
132      * @param value
133      *            the String value.
134      *
135      * @return the index of a new or already existing UTF8 item.
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      * Adds a class reference to the constant pool of the class being built. Does nothing if the constant pool already
155      * contains a similar item.
156      *
157      * @param internalName
158      *            the internal name of the class.
159      *
160      * @return the index of a new or already existing class reference item.
161      */
162     @NonNegative
163     public int newClass(@NonNull String internalName) {
164         return newClassItem(internalName).index;
165     }
166 
167     /**
168      * Adds a class reference to the constant pool of the class being built. Does nothing if the constant pool already
169      * contains a similar item.
170      *
171      * @param internalName
172      *            the internal name of the class.
173      *
174      * @return a new or already existing class reference item.
175      */
176     @NonNull
177     public StringItem newClassItem(@NonNull String internalName) {
178         return newStringItem(CLASS, internalName);
179     }
180 
181     /**
182      * Adds a string to the constant pool of the class being built. Does nothing if the constant pool already contains a
183      * similar item.
184      *
185      * @param type
186      *            one of {@link ConstantPoolTypes#STRING}, {@link ConstantPoolTypes#CLASS} or
187      *            {@link ConstantPoolTypes#METHOD_TYPE}
188      * @param value
189      *            the String value.
190      *
191      * @return a new or already existing string item.
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      * Adds a method handle to the constant pool of the class being built. Does nothing if the constant pool already
212      * contains a similar item.
213      *
214      * @return a new or an already existing method type reference item.
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      * Adds a field reference to the constant pool of the class being built. Does nothing if the constant pool already
257      * contains a similar item.
258      *
259      * @param owner
260      *            the internal name of the field's owner class
261      * @param name
262      *            the field's name
263      * @param desc
264      *            the field's descriptor
265      *
266      * @return a new or already existing field reference item
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      * Adds a method reference to the constant pool of the class being built. Does nothing if the constant pool already
275      * contains a similar item.
276      *
277      * @param owner
278      *            the internal name of the method's owner class
279      * @param name
280      *            the method's name
281      * @param desc
282      *            the method's descriptor
283      * @param itf
284      *            <code>true</code> if <code>owner</code> is an interface
285      *
286      * @return a new or already existing method reference item
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      * Adds an integer to the constant pool of the class being built. Does nothing if the constant pool already contains
296      * a similar item.
297      *
298      * @param value
299      *            the int value
300      *
301      * @return a new or already existing int item
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      * Adds a float to the constant pool of the class being built. Does nothing if the constant pool already contains a
321      * similar item.
322      *
323      * @param value
324      *            the float value
325      *
326      * @return a new or already existing float item
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      * Adds a long to the constant pool of the class being built. Does nothing if the constant pool already contains a
346      * similar item.
347      *
348      * @param value
349      *            the long value
350      *
351      * @return a new or already existing long item
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      * Adds a double to the constant pool of the class being built. Does nothing if the constant pool already contains a
372      * similar item.
373      *
374      * @param value
375      *            the double value
376      *
377      * @return a new or already existing double item
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      * Adds a name and type to the constant pool of the class being built. Does nothing if the constant pool already
398      * contains a similar item.
399      *
400      * @param name
401      *            a name
402      * @param desc
403      *            a type descriptor
404      *
405      * @return the index of a new or already existing name and type item
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      * Adds a number or string constant to the constant pool of the class being built. Does nothing if the constant pool
427      * already contains a similar item.
428      *
429      * @param cst
430      *            the value of the constant to be added to the constant pool, which must be an {@link Integer}, a
431      *            {@link Float}, a {@link Long}, a {@link Double}, a {@link String}, or a {@link JavaType}
432      *
433      * @return a new or already existing constant item with the given value
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      * Adds the given internal name to {@link #typeTable} and returns its index. Does nothing if the type table already
494      * contains this internal name.
495      *
496      * @param type
497      *            the internal name to be added to the type table
498      *
499      * @return the index of this internal name in the type table
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      * Adds the given "uninitialized" type to {@link #typeTable} and returns its index. This method is used for
517      * UNINITIALIZED types, made of an internal name and a bytecode offset.
518      *
519      * @param type
520      *            the internal name to be added to the type table
521      * @param offset
522      *            the bytecode offset of the NEW instruction that created this UNINITIALIZED type value
523      *
524      * @return the index of this internal name in the type table
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      * Returns the index of the common super type of the two given types. This method calls {@link #getCommonSuperClass}
564      * and caches the result in the {@link #items} hash table to speedup future calls with the same parameters.
565      *
566      * @param type1
567      *            index of an internal name in {@link #typeTable}
568      * @param type2
569      *            index of an internal name in {@link #typeTable}
570      *
571      * @return the index of the common super type of the two given types
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      * Returns the common super type of the two given types. The default implementation of this method <i>loads</i> the
594      * two given classes and uses the java.lang.Class methods to find the common super class. It can be overridden to
595      * compute this common super type in other ways, in particular without actually loading any class, or to take into
596      * account the class that is currently being generated by this ClassWriter, which can of course not be loaded since
597      * it is under construction.
598      *
599      * @param type1
600      *            the internal name of a class
601      * @param type2
602      *            the internal name of another class
603      *
604      * @return the internal name of the common super class of the two given classes
605      */
606     @NonNull
607     private static String getCommonSuperClass(@NonNull String type1, @NonNull String type2) {
608         // Reimplemented to avoid "duplicate class definition" errors.
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]; // Normal or Uninitialized
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      * Returns the constant pool's hash table item which is equal to the given item.
650      *
651      * @param key
652      *            a constant pool item
653      *
654      * @return the constant pool's hash table item which is equal to the given item, or <code>null</code> if there is no
655      *         such item
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         // noinspection unchecked
667         return (I) item;
668     }
669 
670     /**
671      * Puts the given item in the constant pool's hash table. The hash table <i>must</i> not already contains this item.
672      *
673      * @param item
674      *            the item to be added to the constant pool's hash table
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             // noinspection NumericCastThatLosesPrecision
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             // noinspection AssignmentToMethodParameter
703             item = next;
704         }
705     }
706 
707     /**
708      * Puts one byte and two shorts into the constant pool.
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         // noinspection NumericCastThatLosesPrecision
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 }