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 net.bytebuddy.jar.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    */

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    */

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    */

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    */

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    */

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    */

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.
249    * @return the {@link Type} corresponding to the given internal name.
250    */

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    */

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    */

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    */

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    */

296   public static Type[] getArgumentTypes(final String methodDescriptor) {
297     // First step: compute the number of argument types in methodDescriptor.
298     int numArgumentTypes = 0;
299     // Skip the first character, which is always a '('.
300     int currentOffset = 1;
301     // Parse the argument types, one at a each loop iteration.
302     while (methodDescriptor.charAt(currentOffset) != ')') {
303       while (methodDescriptor.charAt(currentOffset) == '[') {
304         currentOffset++;
305       }
306       if (methodDescriptor.charAt(currentOffset++) == 'L') {
307         // Skip the argument descriptor content.
308         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
309         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
310       }
311       ++numArgumentTypes;
312     }
313
314     // Second step: create a Type instance for each argument type.
315     Type[] argumentTypes = new Type[numArgumentTypes];
316     // Skip the first character, which is always a '('.
317     currentOffset = 1;
318     // Parse and create the argument types, one at each loop iteration.
319     int currentArgumentTypeIndex = 0;
320     while (methodDescriptor.charAt(currentOffset) != ')') {
321       final int currentArgumentTypeOffset = currentOffset;
322       while (methodDescriptor.charAt(currentOffset) == '[') {
323         currentOffset++;
324       }
325       if (methodDescriptor.charAt(currentOffset++) == 'L') {
326         // Skip the argument descriptor content.
327         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
328         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
329       }
330       argumentTypes[currentArgumentTypeIndex++] =
331           getTypeInternal(methodDescriptor, currentArgumentTypeOffset, currentOffset);
332     }
333     return argumentTypes;
334   }
335
336   /**
337    * Returns the {@link Type} values corresponding to the argument types of the given method.
338    *
339    * @param method a method.
340    * @return the {@link Type} values corresponding to the argument types of the given method.
341    */

342   public static Type[] getArgumentTypes(final Method method) {
343     Class<?>[] classes = method.getParameterTypes();
344     Type[] types = new Type[classes.length];
345     for (int i = classes.length - 1; i >= 0; --i) {
346       types[i] = getType(classes[i]);
347     }
348     return types;
349   }
350
351   /**
352    * Returns the return type of methods of this type. This method should only be used for method
353    * types.
354    *
355    * @return the return type of methods of this type.
356    */

357   public Type getReturnType() {
358     return getReturnType(getDescriptor());
359   }
360
361   /**
362    * Returns the {@link Type} corresponding to the return type of the given method descriptor.
363    *
364    * @param methodDescriptor a method descriptor.
365    * @return the {@link Type} corresponding to the return type of the given method descriptor.
366    */

367   public static Type getReturnType(final String methodDescriptor) {
368     return getTypeInternal(
369         methodDescriptor, getReturnTypeOffset(methodDescriptor), methodDescriptor.length());
370   }
371
372   /**
373    * Returns the {@link Type} corresponding to the return type of the given method.
374    *
375    * @param method a method.
376    * @return the {@link Type} corresponding to the return type of the given method.
377    */

378   public static Type getReturnType(final Method method) {
379     return getType(method.getReturnType());
380   }
381
382   /**
383    * Returns the start index of the return type of the given method descriptor.
384    *
385    * @param methodDescriptor a method descriptor.
386    * @return the start index of the return type of the given method descriptor.
387    */

388   static int getReturnTypeOffset(final String methodDescriptor) {
389     // Skip the first character, which is always a '('.
390     int currentOffset = 1;
391     // Skip the argument types, one at a each loop iteration.
392     while (methodDescriptor.charAt(currentOffset) != ')') {
393       while (methodDescriptor.charAt(currentOffset) == '[') {
394         currentOffset++;
395       }
396       if (methodDescriptor.charAt(currentOffset++) == 'L') {
397         // Skip the argument descriptor content.
398         int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
399         currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
400       }
401     }
402     return currentOffset + 1;
403   }
404
405   /**
406    * Returns the {@link Type} corresponding to the given field or method descriptor.
407    *
408    * @param descriptorBuffer a buffer containing the field or method descriptor.
409    * @param descriptorBegin the beginning index, inclusive, of the field or method descriptor in
410    *     descriptorBuffer.
411    * @param descriptorEnd the end index, exclusive, of the field or method descriptor in
412    *     descriptorBuffer.
413    * @return the {@link Type} corresponding to the given type descriptor.
414    */

415   private static Type getTypeInternal(
416       final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
417     switch (descriptorBuffer.charAt(descriptorBegin)) {
418       case 'V':
419         return VOID_TYPE;
420       case 'Z':
421         return BOOLEAN_TYPE;
422       case 'C':
423         return CHAR_TYPE;
424       case 'B':
425         return BYTE_TYPE;
426       case 'S':
427         return SHORT_TYPE;
428       case 'I':
429         return INT_TYPE;
430       case 'F':
431         return FLOAT_TYPE;
432       case 'J':
433         return LONG_TYPE;
434       case 'D':
435         return DOUBLE_TYPE;
436       case '[':
437         return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd);
438       case 'L':
439         return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1);
440       case '(':
441         return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd);
442       default:
443         throw new IllegalArgumentException();
444     }
445   }
446
447   // -----------------------------------------------------------------------------------------------
448   // Methods to get class names, internal names or descriptors.
449   // -----------------------------------------------------------------------------------------------
450
451   /**
452    * Returns the binary name of the class corresponding to this type. This method must not be used
453    * on method types.
454    *
455    * @return the binary name of the class corresponding to this type.
456    */

457   public String getClassName() {
458     switch (sort) {
459       case VOID:
460         return "void";
461       case BOOLEAN:
462         return "boolean";
463       case CHAR:
464         return "char";
465       case BYTE:
466         return "byte";
467       case SHORT:
468         return "short";
469       case INT:
470         return "int";
471       case FLOAT:
472         return "float";
473       case LONG:
474         return "long";
475       case DOUBLE:
476         return "double";
477       case ARRAY:
478         StringBuilder stringBuilder = new StringBuilder(getElementType().getClassName());
479         for (int i = getDimensions(); i > 0; --i) {
480           stringBuilder.append("[]");
481         }
482         return stringBuilder.toString();
483       case OBJECT:
484       case INTERNAL:
485         return valueBuffer.substring(valueBegin, valueEnd).replace('/', '.');
486       default:
487         throw new AssertionError();
488     }
489   }
490
491   /**
492    * Returns the internal name of the class corresponding to this object or array type. The internal
493    * name of a class is its fully qualified name (as returned by Class.getName(), where '.' are
494    * replaced by '/'). This method should only be used for an object or array type.
495    *
496    * @return the internal name of the class corresponding to this object type.
497    */

498   public String getInternalName() {
499     return valueBuffer.substring(valueBegin, valueEnd);
500   }
501
502   /**
503    * Returns the internal name of the given class. The internal name of a class is its fully
504    * qualified name, as returned by Class.getName(), where '.' are replaced by '/'.
505    *
506    * @param clazz an object or array class.
507    * @return the internal name of the given class.
508    */

509   public static String getInternalName(final Class<?> clazz) {
510     return clazz.getName().replace('.', '/');
511   }
512
513   /**
514    * Returns the descriptor corresponding to this type.
515    *
516    * @return the descriptor corresponding to this type.
517    */

518   public String getDescriptor() {
519     if (sort == OBJECT) {
520       return valueBuffer.substring(valueBegin - 1, valueEnd + 1);
521     } else if (sort == INTERNAL) {
522       return 'L' + valueBuffer.substring(valueBegin, valueEnd) + ';';
523     } else {
524       return valueBuffer.substring(valueBegin, valueEnd);
525     }
526   }
527
528   /**
529    * Returns the descriptor corresponding to the given class.
530    *
531    * @param clazz an object class, a primitive class or an array class.
532    * @return the descriptor corresponding to the given class.
533    */

534   public static String getDescriptor(final Class<?> clazz) {
535     StringBuilder stringBuilder = new StringBuilder();
536     appendDescriptor(clazz, stringBuilder);
537     return stringBuilder.toString();
538   }
539
540   /**
541    * Returns the descriptor corresponding to the given constructor.
542    *
543    * @param constructor a {@link Constructor} object.
544    * @return the descriptor of the given constructor.
545    */

546   public static String getConstructorDescriptor(final Constructor<?> constructor) {
547     StringBuilder stringBuilder = new StringBuilder();
548     stringBuilder.append('(');
549     Class<?>[] parameters = constructor.getParameterTypes();
550     for (Class<?> parameter : parameters) {
551       appendDescriptor(parameter, stringBuilder);
552     }
553     return stringBuilder.append(")V").toString();
554   }
555
556   /**
557    * Returns the descriptor corresponding to the given argument and return types.
558    *
559    * @param returnType the return type of the method.
560    * @param argumentTypes the argument types of the method.
561    * @return the descriptor corresponding to the given argument and return types.
562    */

563   public static String getMethodDescriptor(final Type returnType, final Type... argumentTypes) {
564     StringBuilder stringBuilder = new StringBuilder();
565     stringBuilder.append('(');
566     for (Type argumentType : argumentTypes) {
567       argumentType.appendDescriptor(stringBuilder);
568     }
569     stringBuilder.append(')');
570     returnType.appendDescriptor(stringBuilder);
571     return stringBuilder.toString();
572   }
573
574   /**
575    * Returns the descriptor corresponding to the given method.
576    *
577    * @param method a {@link Method} object.
578    * @return the descriptor of the given method.
579    */

580   public static String getMethodDescriptor(final Method method) {
581     StringBuilder stringBuilder = new StringBuilder();
582     stringBuilder.append('(');
583     Class<?>[] parameters = method.getParameterTypes();
584     for (Class<?> parameter : parameters) {
585       appendDescriptor(parameter, stringBuilder);
586     }
587     stringBuilder.append(')');
588     appendDescriptor(method.getReturnType(), stringBuilder);
589     return stringBuilder.toString();
590   }
591
592   /**
593    * Appends the descriptor corresponding to this type to the given string buffer.
594    *
595    * @param stringBuilder the string builder to which the descriptor must be appended.
596    */

597   private void appendDescriptor(final StringBuilder stringBuilder) {
598     if (sort == OBJECT) {
599       stringBuilder.append(valueBuffer, valueBegin - 1, valueEnd + 1);
600     } else if (sort == INTERNAL) {
601       stringBuilder.append('L').append(valueBuffer, valueBegin, valueEnd).append(';');
602     } else {
603       stringBuilder.append(valueBuffer, valueBegin, valueEnd);
604     }
605   }
606
607   /**
608    * Appends the descriptor of the given class to the given string builder.
609    *
610    * @param clazz the class whose descriptor must be computed.
611    * @param stringBuilder the string builder to which the descriptor must be appended.
612    */

613   private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
614     Class<?> currentClass = clazz;
615     while (currentClass.isArray()) {
616       stringBuilder.append('[');
617       currentClass = currentClass.getComponentType();
618     }
619     if (currentClass.isPrimitive()) {
620       char descriptor;
621       if (currentClass == Integer.TYPE) {
622         descriptor = 'I';
623       } else if (currentClass == Void.TYPE) {
624         descriptor = 'V';
625       } else if (currentClass == Boolean.TYPE) {
626         descriptor = 'Z';
627       } else if (currentClass == Byte.TYPE) {
628         descriptor = 'B';
629       } else if (currentClass == Character.TYPE) {
630         descriptor = 'C';
631       } else if (currentClass == Short.TYPE) {
632         descriptor = 'S';
633       } else if (currentClass == Double.TYPE) {
634         descriptor = 'D';
635       } else if (currentClass == Float.TYPE) {
636         descriptor = 'F';
637       } else if (currentClass == Long.TYPE) {
638         descriptor = 'J';
639       } else {
640         throw new AssertionError();
641       }
642       stringBuilder.append(descriptor);
643     } else {
644       stringBuilder.append('L').append(getInternalName(currentClass)).append(';');
645     }
646   }
647
648   // -----------------------------------------------------------------------------------------------
649   // Methods to get the sort, dimension, size, and opcodes corresponding to a Type or descriptor.
650   // -----------------------------------------------------------------------------------------------
651
652   /**
653    * Returns the sort of this type.
654    *
655    * @return {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, {@link #SHORT}, {@link
656    *     #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, {@link #OBJECT} or
657    *     {@link #METHOD}.
658    */

659   public int getSort() {
660     return sort == INTERNAL ? OBJECT : sort;
661   }
662
663   /**
664    * Returns the number of dimensions of this array type. This method should only be used for an
665    * array type.
666    *
667    * @return the number of dimensions of this array type.
668    */

669   public int getDimensions() {
670     int numDimensions = 1;
671     while (valueBuffer.charAt(valueBegin + numDimensions) == '[') {
672       numDimensions++;
673     }
674     return numDimensions;
675   }
676
677   /**
678    * Returns the size of values of this type. This method must not be used for method types.
679    *
680    * @return the size of values of this type, i.e., 2 for {@code long} and {@code double}, 0 for
681    *     {@code void} and 1 otherwise.
682    */

683   public int getSize() {
684     switch (sort) {
685       case VOID:
686         return 0;
687       case BOOLEAN:
688       case CHAR:
689       case BYTE:
690       case SHORT:
691       case INT:
692       case FLOAT:
693       case ARRAY:
694       case OBJECT:
695       case INTERNAL:
696         return 1;
697       case LONG:
698       case DOUBLE:
699         return 2;
700       default:
701         throw new AssertionError();
702     }
703   }
704
705   /**
706    * Returns the size of the arguments and of the return value of methods of this type. This method
707    * should only be used for method types.
708    *
709    * @return the size of the arguments of the method (plus one for the implicit this argument),
710    *     argumentsSize, and the size of its return value, returnSize, packed into a single int i =
711    *     {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
712    *     i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}).
713    */

714   public int getArgumentsAndReturnSizes() {
715     return getArgumentsAndReturnSizes(getDescriptor());
716   }
717
718   /**
719    * Computes the size of the arguments and of the return value of a method.
720    *
721    * @param methodDescriptor a method descriptor.
722    * @return the size of the arguments of the method (plus one for the implicit this argument),
723    *     argumentsSize, and the size of its return value, returnSize, packed into a single int i =
724    *     {@code (argumentsSize &lt;&lt; 2) | returnSize} (argumentsSize is therefore equal to {@code
725    *     i &gt;&gt; 2}, and returnSize to {@code i &amp; 0x03}).
726    */

727   public static int getArgumentsAndReturnSizes(final String methodDescriptor) {
728     int argumentsSize = 1;
729     // Skip the first character, which is always a '('.
730     int currentOffset = 1;
731     int currentChar = methodDescriptor.charAt(currentOffset);
732     // Parse the argument types and compute their size, one at a each loop iteration.
733     while (currentChar != ')') {
734       if (currentChar == 'J' || currentChar == 'D') {
735         currentOffset++;
736         argumentsSize += 2;
737       } else {
738         while (methodDescriptor.charAt(currentOffset) == '[') {
739           currentOffset++;
740         }
741         if (methodDescriptor.charAt(currentOffset++) == 'L') {
742           // Skip the argument descriptor content.
743           int semiColumnOffset = methodDescriptor.indexOf(';', currentOffset);
744           currentOffset = Math.max(currentOffset, semiColumnOffset + 1);
745         }
746         argumentsSize += 1;
747       }
748       currentChar = methodDescriptor.charAt(currentOffset);
749     }
750     currentChar = methodDescriptor.charAt(currentOffset + 1);
751     if (currentChar == 'V') {
752       return argumentsSize << 2;
753     } else {
754       int returnSize = (currentChar == 'J' || currentChar == 'D') ? 2 : 1;
755       return argumentsSize << 2 | returnSize;
756     }
757   }
758
759   /**
760    * Returns a JVM instruction opcode adapted to this {@link Type}. This method must not be used for
761    * method types.
762    *
763    * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, ISTORE, IALOAD,
764    *     IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, ISHR, IUSHR, IAND, IOR, IXOR and
765    *     IRETURN.
766    * @return an opcode that is similar to the given opcode, but adapted to this {@link Type}. For
767    *     example, if this type is {@code float} and {@code opcode} is IRETURN, this method returns
768    *     FRETURN.
769    */

770   public int getOpcode(final int opcode) {
771     if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
772       switch (sort) {
773         case BOOLEAN:
774         case BYTE:
775           return opcode + (Opcodes.BALOAD - Opcodes.IALOAD);
776         case CHAR:
777           return opcode + (Opcodes.CALOAD - Opcodes.IALOAD);
778         case SHORT:
779           return opcode + (Opcodes.SALOAD - Opcodes.IALOAD);
780         case INT:
781           return opcode;
782         case FLOAT:
783           return opcode + (Opcodes.FALOAD - Opcodes.IALOAD);
784         case LONG:
785           return opcode + (Opcodes.LALOAD - Opcodes.IALOAD);
786         case DOUBLE:
787           return opcode + (Opcodes.DALOAD - Opcodes.IALOAD);
788         case ARRAY:
789         case OBJECT:
790         case INTERNAL:
791           return opcode + (Opcodes.AALOAD - Opcodes.IALOAD);
792         case METHOD:
793         case VOID:
794           throw new UnsupportedOperationException();
795         default:
796           throw new AssertionError();
797       }
798     } else {
799       switch (sort) {
800         case VOID:
801           if (opcode != Opcodes.IRETURN) {
802             throw new UnsupportedOperationException();
803           }
804           return Opcodes.RETURN;
805         case BOOLEAN:
806         case BYTE:
807         case CHAR:
808         case SHORT:
809         case INT:
810           return opcode;
811         case FLOAT:
812           return opcode + (Opcodes.FRETURN - Opcodes.IRETURN);
813         case LONG:
814           return opcode + (Opcodes.LRETURN - Opcodes.IRETURN);
815         case DOUBLE:
816           return opcode + (Opcodes.DRETURN - Opcodes.IRETURN);
817         case ARRAY:
818         case OBJECT:
819         case INTERNAL:
820           if (opcode != Opcodes.ILOAD && opcode != Opcodes.ISTORE && opcode != Opcodes.IRETURN) {
821             throw new UnsupportedOperationException();
822           }
823           return opcode + (Opcodes.ARETURN - Opcodes.IRETURN);
824         case METHOD:
825           throw new UnsupportedOperationException();
826         default:
827           throw new AssertionError();
828       }
829     }
830   }
831
832   // -----------------------------------------------------------------------------------------------
833   // Equals, hashCode and toString.
834   // -----------------------------------------------------------------------------------------------
835
836   /**
837    * Tests if the given object is equal to this type.
838    *
839    * @param object the object to be compared to this type.
840    * @return {@literal trueif the given object is equal to this type.
841    */

842   @Override
843   public boolean equals(final Object object) {
844     if (this == object) {
845       return true;
846     }
847     if (!(object instanceof Type)) {
848       return false;
849     }
850     Type other = (Type) object;
851     if ((sort == INTERNAL ? OBJECT : sort) != (other.sort == INTERNAL ? OBJECT : other.sort)) {
852       return false;
853     }
854     int begin = valueBegin;
855     int end = valueEnd;
856     int otherBegin = other.valueBegin;
857     int otherEnd = other.valueEnd;
858     // Compare the values.
859     if (end - begin != otherEnd - otherBegin) {
860       return false;
861     }
862     for (int i = begin, j = otherBegin; i < end; i++, j++) {
863       if (valueBuffer.charAt(i) != other.valueBuffer.charAt(j)) {
864         return false;
865       }
866     }
867     return true;
868   }
869
870   /**
871    * Returns a hash code value for this type.
872    *
873    * @return a hash code value for this type.
874    */

875   @Override
876   public int hashCode() {
877     int hashCode = 13 * (sort == INTERNAL ? OBJECT : sort);
878     if (sort >= ARRAY) {
879       for (int i = valueBegin, end = valueEnd; i < end; i++) {
880         hashCode = 17 * (hashCode + valueBuffer.charAt(i));
881       }
882     }
883     return hashCode;
884   }
885
886   /**
887    * Returns a string representation of this type.
888    *
889    * @return the descriptor of this type.
890    */

891   @Override
892   public String toString() {
893     return getDescriptor();
894   }
895 }
896