• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm;
29 
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.Method;
32 
33 /**
34  * A Java field or method type. This class can be used to make it easier to manipulate type and
35  * method descriptors.
36  *
37  * @author Eric Bruneton
38  * @author Chris Nokleberg
39  */
40 public final class Type {
41 
42   /** The sort of the {@code void} type. See {@link #getSort}. */
43   public static final int VOID = 0;
44 
45   /** The sort of the {@code boolean} type. See {@link #getSort}. */
46   public static final int BOOLEAN = 1;
47 
48   /** The sort of the {@code char} type. See {@link #getSort}. */
49   public static final int CHAR = 2;
50 
51   /** The sort of the {@code byte} type. See {@link #getSort}. */
52   public static final int BYTE = 3;
53 
54   /** The sort of the {@code short} type. See {@link #getSort}. */
55   public static final int SHORT = 4;
56 
57   /** The sort of the {@code int} type. See {@link #getSort}. */
58   public static final int INT = 5;
59 
60   /** The sort of the {@code float} type. See {@link #getSort}. */
61   public static final int FLOAT = 6;
62 
63   /** The sort of the {@code long} type. See {@link #getSort}. */
64   public static final int LONG = 7;
65 
66   /** The sort of the {@code double} type. See {@link #getSort}. */
67   public static final int DOUBLE = 8;
68 
69   /** The sort of array reference types. See {@link #getSort}. */
70   public static final int ARRAY = 9;
71 
72   /** The sort of object reference types. See {@link #getSort}. */
73   public static final int OBJECT = 10;
74 
75   /** The sort of method types. See {@link #getSort}. */
76   public static final int METHOD = 11;
77 
78   /** The (private) sort of object reference types represented with an internal name. */
79   private static final int INTERNAL = 12;
80 
81   /** The descriptors of the primitive types. */
82   private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
83 
84   /** The {@code void} type. */
85   public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);
86 
87   /** The {@code boolean} type. */
88   public static final Type BOOLEAN_TYPE =
89       new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1);
90 
91   /** The {@code char} type. */
92   public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1);
93 
94   /** The {@code byte} type. */
95   public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1);
96 
97   /** The {@code short} type. */
98   public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1);
99 
100   /** The {@code int} type. */
101   public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1);
102 
103   /** The {@code float} type. */
104   public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1);
105 
106   /** The {@code long} type. */
107   public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1);
108 
109   /** The {@code double} type. */
110   public static final Type DOUBLE_TYPE =
111       new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1);
112 
113   // -----------------------------------------------------------------------------------------------
114   // Fields
115   // -----------------------------------------------------------------------------------------------
116 
117   /**
118    * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE},
119    * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY},
120    * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}.
121    */
122   private final int sort;
123 
124   /**
125    * A buffer containing the value of this field or method type. This value is an internal name for
126    * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other
127    * cases.
128    *
129    * <p>For {@link #OBJECT} types, this field also contains the descriptor: the characters in
130    * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link
131    * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor.
132    */
133   private final String valueBuffer;
134 
135   /**
136    * The beginning index, inclusive, of the value of this Java field or method type in {@link
137    * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types,
138    * and a field or method descriptor in the other cases.
139    */
140   private final int valueBegin;
141 
142   /**
143    * The end index, exclusive, of the value of this Java field or method type in {@link
144    * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types,
145    * and a field or method descriptor in the other cases.
146    */
147   private final int valueEnd;
148 
149   /**
150    * Constructs a reference type.
151    *
152    * @param sort the sort of this type, see {@link #sort}.
153    * @param valueBuffer a buffer containing the value of this field or method type.
154    * @param valueBegin the beginning index, inclusive, of the value of this field or method type in
155    *     valueBuffer.
156    * @param valueEnd the end index, exclusive, of the value of this field or method type in
157    *     valueBuffer.
158    */
Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd)159   private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) {
160     this.sort = sort;
161     this.valueBuffer = valueBuffer;
162     this.valueBegin = valueBegin;
163     this.valueEnd = valueEnd;
164   }
165 
166   // -----------------------------------------------------------------------------------------------
167   // Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc.
168   // -----------------------------------------------------------------------------------------------
169 
170   /**
171    * Returns the {@link Type} corresponding to the given type descriptor.
172    *
173    * @param typeDescriptor a field or method type descriptor.
174    * @return the {@link Type} corresponding to the given type descriptor.
175    */
getType(final String typeDescriptor)176   public static Type getType(final String typeDescriptor) {
177     return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
178   }
179 
180   /**
181    * Returns the {@link Type} corresponding to the given class.
182    *
183    * @param clazz a class.
184    * @return the {@link Type} corresponding to the given class.
185    */
getType(final Class<?> clazz)186   public static Type getType(final Class<?> clazz) {
187     if (clazz.isPrimitive()) {
188       if (clazz == Integer.TYPE) {
189         return INT_TYPE;
190       } else if (clazz == Void.TYPE) {
191         return VOID_TYPE;
192       } else if (clazz == Boolean.TYPE) {
193         return BOOLEAN_TYPE;
194       } else if (clazz == Byte.TYPE) {
195         return BYTE_TYPE;
196       } else if (clazz == Character.TYPE) {
197         return CHAR_TYPE;
198       } else if (clazz == Short.TYPE) {
199         return SHORT_TYPE;
200       } else if (clazz == Double.TYPE) {
201         return DOUBLE_TYPE;
202       } else if (clazz == Float.TYPE) {
203         return FLOAT_TYPE;
204       } else if (clazz == Long.TYPE) {
205         return LONG_TYPE;
206       } else {
207         throw new AssertionError();
208       }
209     } else {
210       return getType(getDescriptor(clazz));
211     }
212   }
213 
214   /**
215    * Returns the method {@link Type} corresponding to the given constructor.
216    *
217    * @param constructor a {@link Constructor} object.
218    * @return the method {@link Type} corresponding to the given constructor.
219    */
getType(final Constructor<?> constructor)220   public static Type getType(final Constructor<?> constructor) {
221     return getType(getConstructorDescriptor(constructor));
222   }
223 
224   /**
225    * Returns the method {@link Type} corresponding to the given method.
226    *
227    * @param method a {@link Method} object.
228    * @return the method {@link Type} corresponding to the given method.
229    */
getType(final Method method)230   public static Type getType(final Method method) {
231     return getType(getMethodDescriptor(method));
232   }
233 
234   /**
235    * Returns the type of the elements of this array type. This method should only be used for an
236    * array type.
237    *
238    * @return Returns the type of the elements of this array type.
239    */
getElementType()240   public Type getElementType() {
241     final int numDimensions = getDimensions();
242     return getTypeInternal(valueBuffer, valueBegin + numDimensions, valueEnd);
243   }
244 
245   /**
246    * Returns the {@link Type} corresponding to the given internal name.
247    *
248    * @param internalName an internal name (see {@link Type#getInternalName()}).
249    * @return the {@link Type} corresponding to the given internal name.
250    */
getObjectType(final String internalName)251   public static Type getObjectType(final String internalName) {
252     return new Type(
253         internalName.charAt(0) == '[' ? ARRAY : INTERNAL, internalName, 0, internalName.length());
254   }
255 
256   /**
257    * Returns the {@link Type} corresponding to the given method descriptor. Equivalent to <code>
258    * Type.getType(methodDescriptor)</code>.
259    *
260    * @param methodDescriptor a method descriptor.
261    * @return the {@link Type} corresponding to the given method descriptor.
262    */
getMethodType(final String methodDescriptor)263   public static Type getMethodType(final String methodDescriptor) {
264     return new Type(METHOD, methodDescriptor, 0, methodDescriptor.length());
265   }
266 
267   /**
268    * Returns the method {@link Type} corresponding to the given argument and return types.
269    *
270    * @param returnType the return type of the method.
271    * @param argumentTypes the argument types of the method.
272    * @return the method {@link Type} corresponding to the given argument and return types.
273    */
getMethodType(final Type returnType, final Type... argumentTypes)274   public static Type getMethodType(final Type returnType, final Type... argumentTypes) {
275     return getType(getMethodDescriptor(returnType, argumentTypes));
276   }
277 
278   /**
279    * Returns the argument types of methods of this type. This method should only be used for method
280    * types.
281    *
282    * @return the argument types of methods of this type.
283    */
getArgumentTypes()284   public Type[] getArgumentTypes() {
285     return getArgumentTypes(getDescriptor());
286   }
287 
288   /**
289    * Returns the {@link Type} values corresponding to the argument types of the given method
290    * descriptor.
291    *
292    * @param methodDescriptor a method descriptor.
293    * @return the {@link Type} values corresponding to the argument types of the given method
294    *     descriptor.
295    */
getArgumentTypes(final String methodDescriptor)296   public static Type[] getArgumentTypes(final String methodDescriptor) {
297     // First step: compute the number of argument types in methodDescriptor.
298     int numArgumentTypes = getArgumentCount(methodDescriptor);
299 
300     // Second step: create a Type instance for each argument type.
301     Type[] argumentTypes = new Type[numArgumentTypes];
302     // Skip the first character, which is always a '('.
303     int currentOffset = 1;
304     // Parse and create the argument types, one at each loop iteration.
305     int currentArgumentTypeIndex = 0;
306     while (methodDescriptor.charAt(currentOffset) != ')') {
307       final int currentArgumentTypeOffset = currentOffset;
308       while (methodDescriptor.charAt(currentOffset) == '[') {
309         currentOffset++;
310       }
311       if (methodDescriptor.charAt(currentOffset++) == 'L') {
312         // Skip the argument descriptor content.
313         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
314         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
315       }
316       argumentTypes[currentArgumentTypeIndex++] =
317           getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
318     }
319     return argumentTypes;
320   }
321 
322   /**
323    * Returns the {@link Type} values corresponding to the argument types of the given method.
324    *
325    * @param method a method.
326    * @return the {@link Type} values corresponding to the argument types of the given method.
327    */
getArgumentTypes(final Method method)328   public static Type[] getArgumentTypes(final Method method) {
329     Class<?>[] classes = method.getParameterTypes();
330     Type[] types = new Type[classes.length];
331     for (int i = classes.length - 1; i >= 0; --i) {
332       types[i] = getType(classes[i]);
333     }
334     return types;
335   }
336 
337   /**
338    * Returns the return type of methods of this type. This method should only be used for method
339    * types.
340    *
341    * @return the return type of methods of this type.
342    */
getReturnType()343   public Type getReturnType() {
344     return getReturnType(getDescriptor());
345   }
346 
347   /**
348    * Returns the {@link Type} corresponding to the return type of the given method descriptor.
349    *
350    * @param methodDescriptor a method descriptor.
351    * @return the {@link Type} corresponding to the return type of the given method descriptor.
352    */
getReturnType(final String methodDescriptor)353   public static Type getReturnType(final String methodDescriptor) {
354     return getTypeInternal(
355         methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length());
356   }
357 
358   /**
359    * Returns the {@link Type} corresponding to the return type of the given method.
360    *
361    * @param method a method.
362    * @return the {@link Type} corresponding to the return type of the given method.
363    */
getReturnType(final Method method)364   public static Type getReturnType(final Method method) {
365     return getType(method.getReturnType());
366   }
367 
368   /**
369    * Returns the start index of the return type of the given method descriptor.
370    *
371    * @param methodDescriptor a method descriptor.
372    * @return the start index of the return type of the given method descriptor.
373    */
getReturnTypeOffset(final String methodDescriptor)374   static int getReturnTypeOffset(final String methodDescriptor) {
375     // Skip the first character, which is always a '('.
376     int currentOffset = 1;
377     // Skip the argument types, one at a each loop iteration.
378     while (methodDescriptor.charAt(currentOffset) != ')') {
379       while (methodDescriptor.charAt(currentOffset) == '[') {
380         currentOffset++;
381       }
382       if (methodDescriptor.charAt(currentOffset++) == 'L') {
383         // Skip the argument descriptor content.
384         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
385         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
386       }
387     }
388     return currentOffset + 1;
389   }
390 
391   /**
392    * Returns the {@link Type} corresponding to the given field or method descriptor.
393    *
394    * @param descriptorBuffer a buffer containing the field or method descriptor.
395    * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in
396    *     descriptorBuffer.
397    * @param descriptorEnd the end index, exclusive, of the field or method descriptor in
398    *     descriptorBuffer.
399    * @return the {@link Type} corresponding to the given type descriptor.
400    */
getTypeInternal( final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd)401   private static Type getTypeInternal(
402       final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
403     switch (descriptorBuffer.charAt(descriptorBegin)) {
404       case 'V':
405         return VOID_TYPE;
406       case 'Z':
407         return BOOLEAN_TYPE;
408       case 'C':
409         return CHAR_TYPE;
410       case 'B':
411         return BYTE_TYPE;
412       case 'S':
413         return SHORT_TYPE;
414       case 'I':
415         return INT_TYPE;
416       case 'F':
417         return FLOAT_TYPE;
418       case 'J':
419         return LONG_TYPE;
420       case 'D':
421         return DOUBLE_TYPE;
422       case '[':
423         return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd);
424       case 'L':
425         return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1);
426       case '(':
427         return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd);
428       default:
429         throw new IllegalArgumentException("Invalid descriptor: " + descriptorBuffer);
430     }
431   }
432 
433   // -----------------------------------------------------------------------------------------------
434   // Methods to get class names, internal names or descriptors.
435   // -----------------------------------------------------------------------------------------------
436 
437   /**
438    * Returns the binary name of the class corresponding to this type. This method must not be used
439    * on method types.
440    *
441    * @return the binary name of the class corresponding to this type.
442    */
getClassName()443   public String getClassName() {
444     switch (sort) {
445       case VOID:
446         return "void";
447       case BOOLEAN:
448         return "boolean";
449       case CHAR:
450         return "char";
451       case BYTE:
452         return "byte";
453       case SHORT:
454         return "short";
455       case INT:
456         return "int";
457       case FLOAT:
458         return "float";
459       case LONG:
460         return "long";
461       case DOUBLE:
462         return "double";
463       case ARRAY:
464         StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName());
465         for (int i = getDimensions(); i > 0; --i) {
466           stringBuilder.append("[]");
467         }
468         return stringBuilder.toString();
469       case OBJECT:
470       case INTERNAL:
471         return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.');
472       default:
473         throw new AssertionError();
474     }
475   }
476 
477   /**
478    * Returns the internal name of the class corresponding to this object or array type. The internal
479    * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are
480    * replaced by '/'). This method should only be used for an object or array type.
481    *
482    * @return the internal name of the class corresponding to this object type.
483    */
getInternalName()484   public String getInternalName() {
485     return valueBuffer.substring(valueBegin, valueEnd);
486   }
487 
488   /**
489    * Returns the internal name of the given class. The internal name of a class is its fully
490    * qualified name, as returned by Class.getName(), where '.' are replaced by '/'.
491    *
492    * @param clazz an object or array class.
493    * @return the internal name of the given class.
494    */
getInternalName(final Class<?> clazz)495   public static String getInternalName(final Class<?> clazz) {
496     return clazz.getName().replace('.', '/');
497   }
498 
499   /**
500    * Returns the descriptor corresponding to this type.
501    *
502    * @return the descriptor corresponding to this type.
503    */
getDescriptor()504   public String getDescriptor() {
505     if (sort == OBJECT) {
506       return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
507     } else if (sort == INTERNAL) {
508       return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
509     } else {
510       return valueBuffer.substring(valueBegin, valueEnd);
511     }
512   }
513 
514   /**
515    * Returns the descriptor corresponding to the given class.
516    *
517    * @param clazz an object class, a primitive class or an array class.
518    * @return the descriptor corresponding to the given class.
519    */
getDescriptor(final Class<?> clazz)520   public static String getDescriptor(final Class<?> clazz) {
521     StringBuilder stringBuilder = new StringBuilder();
522     appendDescriptor(clazz, stringBuilder);
523     return stringBuilder.toString();
524   }
525 
526   /**
527    * Returns the descriptor corresponding to the given constructor.
528    *
529    * @param constructor a {@link Constructor} object.
530    * @return the descriptor of the given constructor.
531    */
getConstructorDescriptor(final Constructor<?> constructor)532   public static String getConstructorDescriptor(final Constructor<?> constructor) {
533     StringBuilder stringBuilder = new StringBuilder();
534     stringBuilder.append('(');
535     Class<?>[] parameters = constructor.getParameterTypes();
536     for (Class<?> parameter : parameters) {
537       appendDescriptor(parameter, stringBuilder);
538     }
539     return stringBuilder.append(")V").toString();
540   }
541 
542   /**
543    * Returns the descriptor corresponding to the given argument and return types.
544    *
545    * @param returnType the return type of the method.
546    * @param argumentTypes the argument types of the method.
547    * @return the descriptor corresponding to the given argument and return types.
548    */
getMethodDescriptor(final Type returnType, final Type... argumentTypes)549   public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) {
550     StringBuilder stringBuilder = new StringBuilder();
551     stringBuilder.append('(');
552     for (Type argumentType : argumentTypes) {
553       argumentType.appendDescriptor(stringBuilder);
554     }
555     stringBuilder.append(')');
556     returnType.appendDescriptor(stringBuilder);
557     return stringBuilder.toString();
558   }
559 
560   /**
561    * Returns the descriptor corresponding to the given method.
562    *
563    * @param method a {@link Method} object.
564    * @return the descriptor of the given method.
565    */
getMethodDescriptor(final Method method)566   public static String getMethodDescriptor(final Method method) {
567     StringBuilder stringBuilder = new StringBuilder();
568     stringBuilder.append('(');
569     Class<?>[] parameters = method.getParameterTypes();
570     for (Class<?> parameter : parameters) {
571       appendDescriptor(parameter, stringBuilder);
572     }
573     stringBuilder.append(')');
574     appendDescriptor(method.getReturnType(), stringBuilder);
575     return stringBuilder.toString();
576   }
577 
578   /**
579    * Appends the descriptor corresponding to this type to the given string buffer.
580    *
581    * @param stringBuilder the string builder to which the descriptor must be appended.
582    */
appendDescriptor(final StringBuilder stringBuilder)583   private void appendDescriptor(final StringBuilder stringBuilder) {
584     if (sort == OBJECT) {
585       stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1);
586     } else if (sort == INTERNAL) {
587       stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';');
588     } else {
589       stringBuilder.append(valueBuffer, valueBegin, valueEnd);
590     }
591   }
592 
593   /**
594    * Appends the descriptor of the given class to the given string builder.
595    *
596    * @param clazz the class whose descriptor must be computed.
597    * @param stringBuilder the string builder to which the descriptor must be appended.
598    */
appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder)599   private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
600     Class<?> currentClass = clazz;
601     while (currentClass.isArray()) {
602       stringBuilder.append('[');
603       currentClass = currentClass.getComponentType();
604     }
605     if (currentClass.isPrimitive()) {
606       char descriptor;
607       if (currentClass == Integer.TYPE) {
608         descriptor = 'I';
609       } else if (currentClass == Void.TYPE) {
610         descriptor = 'V';
611       } else if (currentClass == Boolean.TYPE) {
612         descriptor = 'Z';
613       } else if (currentClass == Byte.TYPE) {
614         descriptor = 'B';
615       } else if (currentClass == Character.TYPE) {
616         descriptor = 'C';
617       } else if (currentClass == Short.TYPE) {
618         descriptor = 'S';
619       } else if (currentClass == Double.TYPE) {
620         descriptor = 'D';
621       } else if (currentClass == Float.TYPE) {
622         descriptor = 'F';
623       } else if (currentClass == Long.TYPE) {
624         descriptor = 'J';
625       } else {
626         throw new AssertionError();
627       }
628       stringBuilder.append(descriptor);
629     } else {
630       stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
631     }
632   }
633 
634   // -----------------------------------------------------------------------------------------------
635   // Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor.
636   // -----------------------------------------------------------------------------------------------
637 
638   /**
639    * Returns the sort of this type.
640    *
641    * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link
642    *     #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or
643    *     {@link #METHOD}.
644    */
getSort()645   public int getSort() {
646     return sort == INTERNAL ? OBJECT : sort;
647   }
648 
649   /**
650    * Returns the number of dimensions of this array type. This method should only be used for an
651    * array type.
652    *
653    * @return the number of dimensions of this array type.
654    */
getDimensions()655   public int getDimensions() {
656     int numDimensions = 1;
657     while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
658       numDimensions++;
659     }
660     return numDimensions;
661   }
662 
663   /**
664    * Returns the size of values of this type. This method must not be used for method types.
665    *
666    * @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for
667    *     {@code void} and 1 otherwise.
668    */
getSize()669   public int getSize() {
670     switch (sort) {
671       case VOID:
672         return 0;
673       case BOOLEAN:
674       case CHAR:
675       case BYTE:
676       case SHORT:
677       case INT:
678       case FLOAT:
679       case ARRAY:
680       case OBJECT:
681       case INTERNAL:
682         return 1;
683       case LONG:
684       case DOUBLE:
685         return 2;
686       default:
687         throw new AssertionError();
688     }
689   }
690 
691   /**
692    * Returns the number of arguments of this method type. This method should only be used for method
693    * types.
694    *
695    * @return the number of arguments of this method type. Each argument counts for 1, even long and
696    *     double ones. The implicit @literal{this} argument is not counted.
697    */
getArgumentCount()698   public int getArgumentCount() {
699     return getArgumentCount(getDescriptor());
700   }
701 
702   /**
703    * Returns the number of arguments in the given method descriptor.
704    *
705    * @param methodDescriptor a method descriptor.
706    * @return the number of arguments in the given method descriptor. Each argument counts for 1,
707    *     even long and double ones. The implicit @literal{this} argument is not counted.
708    */
getArgumentCount(final String methodDescriptor)709   public static int getArgumentCount(final String methodDescriptor) {
710     int argumentCount = 0;
711     // Skip the first character, which is always a '('.
712     int currentOffset = 1;
713     // Parse the argument types, one at a each loop iteration.
714     while (methodDescriptor.charAt(currentOffset) != ')') {
715       while (methodDescriptor.charAt(currentOffset) == '[') {
716         currentOffset++;
717       }
718       if (methodDescriptor.charAt(currentOffset++) == 'L') {
719         // Skip the argument descriptor content.
720         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
721         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
722       }
723       ++argumentCount;
724     }
725     return argumentCount;
726   }
727 
728   /**
729    * Returns the size of the arguments and of the return value of methods of this type. This method
730    * should only be used for method types.
731    *
732    * @return the size of the arguments of the method (plus one for the implicit this argument),
733    *     argumentsSize, and the size of its return value, returnSize, packed into a single int i =
734    *     {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
735    *     i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}). Long and double values have size 2,
736    *     the others have size 1.
737    */
getArgumentsAndReturnSizes()738   public int getArgumentsAndReturnSizes() {
739     return getArgumentsAndReturnSizes(getDescriptor());
740   }
741 
742   /**
743    * Computes the size of the arguments and of the return value of a method.
744    *
745    * @param methodDescriptor a method descriptor.
746    * @return the size of the arguments of the method (plus one for the implicit this argument),
747    *     argumentsSize, and the size of its return value, returnSize, packed into a single int i =
748    *     {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
749    *     i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}). Long and double values have size 2,
750    *     the others have size 1.
751    */
getArgumentsAndReturnSizes(final String methodDescriptor)752   public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
753     int argumentsSize = 1;
754     // Skip the first character, which is always a '('.
755     int currentOffset = 1;
756     int currentChar = methodDescriptor.charAt(currentOffset);
757     // Parse the argument types and compute their size, one at a each loop iteration.
758     while (currentChar != ')') {
759       if (currentChar == 'J' || currentChar == 'D') {
760         currentOffset++;
761         argumentsSize += 2;
762       } else {
763         while (methodDescriptor.charAt(currentOffset) == '[') {
764           currentOffset++;
765         }
766         if (methodDescriptor.charAt(currentOffset++) == 'L') {
767           // Skip the argument descriptor content.
768           int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
769           currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
770         }
771         argumentsSize += 1;
772       }
773       currentChar = methodDescriptor.charAt(currentOffset);
774     }
775     currentChar = methodDescriptor.charAt(currentOffset + 1);
776     if (currentChar == 'V') {
777       return argumentsSize << 2;
778     } else {
779       int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
780       return argumentsSize << 2 | returnSize;
781     }
782   }
783 
784   /**
785    * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for
786    * method types.
787    *
788    * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD,
789    *     IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and
790    *     IRETURN.
791    * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For
792    *     example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns
793    *     FRETURN.
794    */
getOpcode(final int opcode)795   public int getOpcode(final int opcode) {
796     if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
797       switch (sort) {
798         case BOOLEAN:
799         case BYTE:
800           return opcode + (Opcodes.BALOAD - Opcodes.IALOAD);
801         case CHAR:
802           return opcode + (Opcodes.CALOAD - Opcodes.IALOAD);
803         case SHORT:
804           return opcode + (Opcodes.SALOAD - Opcodes.IALOAD);
805         case INT:
806           return opcode;
807         case FLOAT:
808           return opcode + (Opcodes.FALOAD - Opcodes.IALOAD);
809         case LONG:
810           return opcode + (Opcodes.LALOAD - Opcodes.IALOAD);
811         case DOUBLE:
812           return opcode + (Opcodes.DALOAD - Opcodes.IALOAD);
813         case ARRAY:
814         case OBJECT:
815         case INTERNAL:
816           return opcode + (Opcodes.AALOAD - Opcodes.IALOAD);
817         case METHOD:
818         case VOID:
819           throw new UnsupportedOperationException();
820         default:
821           throw new AssertionError();
822       }
823     } else {
824       switch (sort) {
825         case VOID:
826           if (opcode != Opcodes.IRETURN) {
827             throw new UnsupportedOperationException();
828           }
829           return Opcodes.RETURN;
830         case BOOLEAN:
831         case BYTE:
832         case CHAR:
833         case SHORT:
834         case INT:
835           return opcode;
836         case FLOAT:
837           return opcode + (Opcodes.FRETURN - Opcodes.IRETURN);
838         case LONG:
839           return opcode + (Opcodes.LRETURN - Opcodes.IRETURN);
840         case DOUBLE:
841           return opcode + (Opcodes.DRETURN - Opcodes.IRETURN);
842         case ARRAY:
843         case OBJECT:
844         case INTERNAL:
845           if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) {
846             throw new UnsupportedOperationException();
847           }
848           return opcode + (Opcodes.ARETURN - Opcodes.IRETURN);
849         case METHOD:
850           throw new UnsupportedOperationException();
851         default:
852           throw new AssertionError();
853       }
854     }
855   }
856 
857   // -----------------------------------------------------------------------------------------------
858   // Equals, hashCode and toString.
859   // -----------------------------------------------------------------------------------------------
860 
861   /**
862    * Tests if the given object is equal to this type.
863    *
864    * @param object the object to be compared to this type.
865    * @return {@literal true} if the given object is equal to this type.
866    */
867   @Override
equals(final Object object)868   public boolean equals(final Object object) {
869     if (this == object) {
870       return true;
871     }
872     if (!(object instanceof Type)) {
873       return false;
874     }
875     Type other = (Type) object;
876     if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) {
877       return false;
878     }
879     int begin = valueBegin;
880     int end = valueEnd;
881     int otherBegin = other.valueBegin;
882     int otherEnd = other.valueEnd;
883     // Compare the values.
884     if (end - begin != otherEnd - otherBegin) {
885       return false;
886     }
887     for (int i = begin, j = otherBegin; i < end; i++, j++) {
888       if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) {
889         return false;
890       }
891     }
892     return true;
893   }
894 
895   /**
896    * Returns a hash code value for this type.
897    *
898    * @return a hash code value for this type.
899    */
900   @Override
hashCode()901   public int hashCode() {
902     int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort);
903     if (sort >= ARRAY) {
904       for (int i = valueBegin, end = valueEnd; i < end; i++) {
905         hashCode = 17 * (hashCode + valueBuffer.charAt(i));
906       }
907     }
908     return hashCode;
909   }
910 
911   /**
912    * Returns a string representation of this type.
913    *
914    * @return the descriptor of this type.
915    */
916   @Override
toString()917   public String toString() {
918     return getDescriptor();
919   }
920 }
921