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