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.types;
7   
8   import edu.umd.cs.findbugs.annotations.NonNull;
9   
10  import java.lang.reflect.Constructor;
11  import java.lang.reflect.Method;
12  
13  import org.checkerframework.checker.index.qual.NonNegative;
14  
15  /**
16   * A Java field or method type. This class can be used to make it easier to manipulate type and method descriptors.
17   */
18  @SuppressWarnings("ClassReferencesSubclass")
19  public abstract class JavaType {
20      private static final JavaType[] NO_ARGS = {};
21  
22      /**
23       * The length of the internal name of this Java type.
24       */
25      @NonNegative
26      final int len;
27  
28      /**
29       * Constructs a Java type.
30       *
31       * @param len
32       *            the length of this descriptor.
33       */
34      JavaType(@NonNegative int len) {
35          this.len = len;
36      }
37  
38      /**
39       * Returns the Java type corresponding to the given type descriptor.
40       *
41       * @param typeDescriptor
42       *            a field or method type descriptor.
43       */
44      @NonNull
45      public static JavaType getType(@NonNull String typeDescriptor) {
46          return getType(typeDescriptor.toCharArray(), 0);
47      }
48  
49      /**
50       * Returns the Java types corresponding to the argument types of the given method descriptor.
51       */
52      @NonNull
53      public static JavaType[] getArgumentTypes(@NonNull String methodDescriptor) {
54          char[] buf = methodDescriptor.toCharArray();
55          int off = 1;
56          int size = 0;
57  
58          while (true) {
59              char c = buf[off];
60              off++;
61  
62              if (c == ')') {
63                  break;
64              }
65              if (c == 'L') {
66                  off = findNextTypeTerminatorCharacter(buf, off);
67                  size++;
68              } else if (c != '[') {
69                  size++;
70              }
71          }
72  
73          return getArgumentTypes(buf, size);
74      }
75  
76      @NonNegative
77      private static int findNextTypeTerminatorCharacter(@NonNull char[] desc, @NonNegative int i) {
78          while (desc[i++] != ';') {
79          }
80          return i;
81      }
82  
83      @NonNull
84      private static JavaType[] getArgumentTypes(@NonNull char[] buf, @NonNegative int argCount) {
85          if (argCount == 0) {
86              return NO_ARGS;
87          }
88  
89          JavaType[] argTypes = new JavaType[argCount];
90          int off = 1;
91  
92          for (int i = 0; buf[off] != ')'; i++) {
93              JavaType argType = getType(buf, off);
94              argTypes[i] = argType;
95              off += argType.len + (argType instanceof ObjectType ? 2 : 0);
96          }
97  
98          return argTypes;
99      }
100 
101     /**
102      * Returns the Java type corresponding to the return type of the given method descriptor.
103      */
104     @NonNull
105     public static JavaType getReturnType(@NonNull String methodDescriptor) {
106         char[] buf = methodDescriptor.toCharArray();
107         return getType(buf, methodDescriptor.indexOf(')') + 1);
108     }
109 
110     /**
111      * Computes the size of the arguments and of the return value of a method.
112      *
113      * @param desc
114      *            the descriptor of a method.
115      *
116      * @return the size of the arguments of the method (plus one for the implicit <code>this</code> argument),
117      *         <code>argSize</code>, and the size of its return value, <code>retSize</code>, packed into a single
118      *
119      *         <pre>{@code int i = (argSize << 2) | retSize }</pre>
120      *
121      *         (<code>argSize</code> is therefore equal to
122      *
123      *         <pre>{@code i >> 2 }</pre>
124      *
125      *         , and
126      *
127      *         <pre>{@code retSize }</pre>
128      *
129      *         to
130      *
131      *         <pre>
132      * &#64;{code i &amp; 0x03 }
133      *         </pre>
134      *
135      *         ).
136      */
137     public static int getArgumentsAndReturnSizes(@NonNull String desc) {
138         int argSize = 1;
139         int i = 1;
140 
141         while (true) {
142             char currentChar = desc.charAt(i);
143             i++;
144 
145             switch (currentChar) {
146                 case ')': {
147                     char nextChar = desc.charAt(i);
148                     return argSize << 2 | (nextChar == 'V' ? 0 : isDoubleSizePrimitiveType(nextChar) ? 2 : 1);
149                 }
150                 case 'L':
151                     i = findNextTypeTerminatorCharacter(desc, i);
152                     argSize++;
153                     break;
154                 case '[': {
155                     i = findStartOfArrayElementType(desc, i);
156                     char arrayElementType = desc.charAt(i);
157                     if (isDoubleSizePrimitiveType(arrayElementType)) {
158                         argSize--;
159                     }
160                     break;
161                 }
162                 default:
163                     if (isDoubleSizePrimitiveType(currentChar)) {
164                         argSize += 2;
165                     } else {
166                         argSize++;
167                     }
168                     break;
169             }
170         }
171     }
172 
173     private static boolean isDoubleSizePrimitiveType(char typeCode) {
174         return typeCode == 'D' || typeCode == 'J';
175     }
176 
177     @NonNegative
178     private static int findNextTypeTerminatorCharacter(@NonNull String desc, @NonNegative int i) {
179         while (desc.charAt(i++) != ';') {
180         }
181         return i;
182     }
183 
184     @NonNegative
185     private static int findStartOfArrayElementType(@NonNull String desc, @NonNegative int i) {
186         while (desc.charAt(i) == '[') {
187             i++;
188         }
189         return i;
190     }
191 
192     /**
193      * Returns the Java type corresponding to the given type descriptor. For method descriptors, <code>buf</code> is
194      * supposed to contain nothing more than the descriptor itself.
195      *
196      * @param buf
197      *            a buffer containing a type descriptor.
198      * @param off
199      *            the offset of this descriptor in the previous buffer.
200      */
201     @NonNull
202     static JavaType getType(@NonNull char[] buf, @NonNegative int off) {
203         PrimitiveType primitiveType = PrimitiveType.getPrimitiveType(buf[off]);
204 
205         if (primitiveType != null) {
206             return primitiveType;
207         }
208 
209         return ReferenceType.getReferenceType(buf, off);
210     }
211 
212     /**
213      * Returns the binary name of the class corresponding to this type. This method must not be used on method types.
214      */
215     @NonNull
216     public abstract String getClassName();
217 
218     // ------------------------------------------------------------------------
219     // Conversion to type descriptors
220     // ------------------------------------------------------------------------
221 
222     /**
223      * Returns the descriptor corresponding to this Java type.
224      */
225     @NonNull
226     public final String getDescriptor() {
227         StringBuilder buf = new StringBuilder();
228         getDescriptor(buf);
229         return buf.toString();
230     }
231 
232     /**
233      * Appends the descriptor corresponding to this Java type to the given string buffer.
234      *
235      * @param typeDesc
236      *            the string builder to which the descriptor must be appended
237      */
238     abstract void getDescriptor(@NonNull StringBuilder typeDesc);
239 
240     // -------------------------------------------------------------------------------------------------------
241     // Direct conversion from classes to type descriptors, and vice-versa, without intermediate JavaType objects
242     // -------------------------------------------------------------------------------------------------------
243 
244     /**
245      * Returns the internal name of the given class. The internal name of a class is its fully qualified name, as
246      * returned by Class.getName(), where '.' are replaced by '/'.
247      *
248      * @param aClass
249      *            an object or array class
250      */
251     @NonNull
252     public static String getInternalName(@NonNull Class<?> aClass) {
253         return aClass.getName().replace('.', '/');
254     }
255 
256     /**
257      * Returns the descriptor corresponding to the given constructor.
258      */
259     @NonNull
260     public static String getConstructorDescriptor(@NonNull Constructor<?> constructor) {
261         StringBuilder buf = getMemberDescriptor(constructor.getParameterTypes());
262         buf.append('V');
263         return buf.toString();
264     }
265 
266     @NonNull
267     private static StringBuilder getMemberDescriptor(@NonNull Class<?>[] parameterTypes) {
268         StringBuilder buf = new StringBuilder();
269         buf.append('(');
270 
271         for (Class<?> parameterType : parameterTypes) {
272             getDescriptor(buf, parameterType);
273         }
274 
275         buf.append(')');
276         return buf;
277     }
278 
279     /**
280      * Returns the descriptor corresponding to the given method.
281      */
282     @NonNull
283     public static String getMethodDescriptor(@NonNull Method method) {
284         StringBuilder buf = getMemberDescriptor(method.getParameterTypes());
285         getDescriptor(buf, method.getReturnType());
286         return buf.toString();
287     }
288 
289     /**
290      * Appends the descriptor of the given class to the given string builder.
291      */
292     private static void getDescriptor(@NonNull StringBuilder buf, @NonNull Class<?> aClass) {
293         Class<?> d = aClass;
294 
295         while (true) {
296             if (d.isPrimitive()) {
297                 char typeCode = PrimitiveType.getPrimitiveType(d).getTypeCode();
298                 buf.append(typeCode);
299                 return;
300             }
301             if (!d.isArray()) {
302                 ReferenceType.getDescriptor(buf, d);
303                 return;
304             }
305             buf.append('[');
306             d = d.getComponentType();
307         }
308     }
309 
310     // ------------------------------------------------------------------------
311     // Corresponding size and opcodes
312     // ------------------------------------------------------------------------
313 
314     /**
315      * Returns the size of values of this type. This method must not be used for method types.
316      *
317      * @return the size of values of this type, i.e., 2 for <code>long</code> and <code>double</code>, 0 for
318      *         <code>void</code> and 1 otherwise.
319      */
320     @NonNegative
321     public abstract int getSize();
322 
323     /**
324      * Returns a JVM instruction opcode adapted to this Java type. This method must not be used for method types.
325      *
326      * @param opcode
327      *            a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL,
328      *            IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
329      *
330      * @return an opcode that is similar to the given opcode, but adapted to this Java type. For example, if this type
331      *         is <code>float</code> and <code>opcode</code> is IRETURN, this method returns FRETURN.
332      */
333     public abstract int getOpcode(int opcode);
334 
335     public abstract int getLoadOpcode();
336 
337     public abstract int getConstOpcode();
338 
339     /**
340      * Returns a string representation of this type.
341      *
342      * @return the descriptor of this type.
343      */
344     @Override
345     public final String toString() {
346         return getDescriptor();
347     }
348 }