View Javadoc
1   package mockit.asm.classes;
2   
3   import static mockit.asm.jvmConstants.ConstantPoolTypes.INVOKE_DYNAMIC;
4   
5   import edu.umd.cs.findbugs.annotations.NonNull;
6   
7   import mockit.asm.constantPool.AttributeWriter;
8   import mockit.asm.constantPool.BootstrapMethodItem;
9   import mockit.asm.constantPool.ConstantPoolGeneration;
10  import mockit.asm.constantPool.DynamicItem;
11  import mockit.asm.constantPool.Item;
12  import mockit.asm.constantPool.MethodHandleItem;
13  import mockit.asm.util.ByteVector;
14  import mockit.asm.util.MethodHandle;
15  
16  import org.checkerframework.checker.index.qual.NonNegative;
17  
18  /**
19   * Generates the "BootstrapMethods" attribute in a class file being written by a {@link ClassWriter}.
20   */
21  final class BootstrapMethodsWriter extends AttributeWriter {
22      @NonNull
23      private final ByteVector bootstrapMethods;
24      @NonNegative
25      private final int bootstrapMethodsCount;
26      @NonNegative
27      private final int bsmStartCodeIndex;
28  
29      BootstrapMethodsWriter(@NonNull ConstantPoolGeneration cp, @NonNull ClassReader cr) {
30          super(cp);
31  
32          int attrSize = cr.readInt();
33          bootstrapMethods = new ByteVector(attrSize + 62);
34          bootstrapMethodsCount = cr.readUnsignedShort();
35  
36          bsmStartCodeIndex = cr.codeIndex;
37          bootstrapMethods.putByteArray(cr.code, bsmStartCodeIndex, attrSize - 2);
38      }
39  
40      /**
41       * Copies the bootstrap method data from the given {@link ClassReader}.
42       */
43      void copyBootstrapMethods(@NonNull ClassReader cr, @NonNull Item[] items) {
44          int previousCodeIndex = cr.codeIndex;
45          cr.codeIndex = bsmStartCodeIndex;
46  
47          for (int bsmIndex = 0, bsmCount = bootstrapMethodsCount; bsmIndex < bsmCount; bsmIndex++) {
48              copyBootstrapMethod(cr, items, bsmIndex);
49          }
50  
51          cr.codeIndex = previousCodeIndex;
52      }
53  
54      private void copyBootstrapMethod(@NonNull ClassReader cr, @NonNull Item[] items, @NonNegative int bsmIndex) {
55          int position = cr.codeIndex - bsmStartCodeIndex;
56          MethodHandle bsm = cr.readMethodHandle();
57          int hashCode = bsm.hashCode();
58  
59          for (int bsmArgCount = cr.readUnsignedShort(); bsmArgCount > 0; bsmArgCount--) {
60              Object bsmArg = cr.readConstItem();
61              hashCode ^= bsmArg.hashCode();
62          }
63  
64          BootstrapMethodItem item = new BootstrapMethodItem(bsmIndex, position, hashCode);
65          item.setNext(items);
66      }
67  
68      /**
69       * Adds an invokedynamic reference to the constant pool of the class being built. Does nothing if the constant pool
70       * already contains a similar item.
71       *
72       * @param name
73       *            name of the invoked method
74       * @param desc
75       *            descriptor of the invoke method
76       * @param bsm
77       *            the bootstrap method
78       * @param bsmArgs
79       *            the bootstrap method constant arguments
80       *
81       * @return a new or an already existing invokedynamic type reference item
82       */
83      @NonNull
84      DynamicItem addInvokeDynamicReference(@NonNull String name, @NonNull String desc, @NonNull MethodHandle bsm,
85              @NonNull Object... bsmArgs) {
86          ByteVector methods = bootstrapMethods;
87          int position = methods.getLength(); // record current position
88  
89          MethodHandleItem methodHandleItem = cp.newMethodHandleItem(bsm);
90          methods.putShort(methodHandleItem.index);
91  
92          int argsLength = bsmArgs.length;
93          methods.putShort(argsLength);
94  
95          int hashCode = bsm.hashCode();
96          hashCode = putBSMArgs(hashCode, bsmArgs);
97          hashCode &= 0x7FFFFFFF;
98  
99          methods.setLength(position); // revert to old position
100 
101         BootstrapMethodItem bsmItem = getBSMItem(hashCode);
102         return cp.createDynamicItem(INVOKE_DYNAMIC, name, desc, bsmItem.index);
103     }
104 
105     private int putBSMArgs(int hashCode, @NonNull Object[] bsmArgs) {
106         for (Object bsmArg : bsmArgs) {
107             hashCode ^= bsmArg.hashCode();
108 
109             Item constItem = cp.newConstItem(bsmArg);
110             bootstrapMethods.putShort(constItem.index);
111         }
112 
113         return hashCode;
114     }
115 
116     @NonNull
117     private BootstrapMethodItem getBSMItem(@NonNegative int hashCode) {
118         Item item = cp.getItem(hashCode);
119 
120         while (item != null) {
121             if (item instanceof BootstrapMethodItem && item.getHashCode() == hashCode) {
122                 return (BootstrapMethodItem) item;
123             }
124 
125             item = item.getNext();
126         }
127 
128         throw new IllegalStateException("BootstrapMethodItem not found for hash code " + hashCode);
129     }
130 
131     @NonNegative
132     @Override
133     public int getSize() {
134         return 8 + bootstrapMethods.getLength();
135     }
136 
137     @Override
138     public void put(@NonNull ByteVector out) {
139         setAttribute("BootstrapMethods");
140         put(out, 2 + bootstrapMethods.getLength());
141         out.putShort(bootstrapMethodsCount);
142         out.putByteVector(bootstrapMethods);
143     }
144 }