001// ASM: a very small and fast Java bytecode manipulation framework
002// Copyright (c) 2000-2011 INRIA, France Telecom
003// All rights reserved.
004//
005// Redistribution and use in source and binary forms, with or without
006// modification, are permitted provided that the following conditions
007// are met:
008// 1. Redistributions of source code must retain the above copyright
009//    notice, this list of conditions and the following disclaimer.
010// 2. Redistributions in binary form must reproduce the above copyright
011//    notice, this list of conditions and the following disclaimer in the
012//    documentation and/or other materials provided with the distribution.
013// 3. Neither the name of the copyright holders nor the names of its
014//    contributors may be used to endorse or promote products derived from
015//    this software without specific prior written permission.
016//
017// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
027// THE POSSIBILITY OF SUCH DAMAGE.
028package org.springframework.asm;
029
030import java.lang.reflect.Constructor;
031import java.lang.reflect.Method;
032
033/**
034 * A Java field or method type. This class can be used to make it easier to manipulate type and
035 * method descriptors.
036 *
037 * @author Eric Bruneton
038 * @author Chris Nokleberg
039 */
040public final class Type {
041
042  /** The sort of the {@code void} type. See {@link #getSort}. */
043  public static final int VOID = 0;
044
045  /** The sort of the {@code boolean} type. See {@link #getSort}. */
046  public static final int BOOLEAN = 1;
047
048  /** The sort of the {@code char} type. See {@link #getSort}. */
049  public static final int CHAR = 2;
050
051  /** The sort of the {@code byte} type. See {@link #getSort}. */
052  public static final int BYTE = 3;
053
054  /** The sort of the {@code short} type. See {@link #getSort}. */
055  public static final int SHORT = 4;
056
057  /** The sort of the {@code int} type. See {@link #getSort}. */
058  public static final int INT = 5;
059
060  /** The sort of the {@code float} type. See {@link #getSort}. */
061  public static final int FLOAT = 6;
062
063  /** The sort of the {@code long} type. See {@link #getSort}. */
064  public static final int LONG = 7;
065
066  /** The sort of the {@code double} type. See {@link #getSort}. */
067  public static final int DOUBLE = 8;
068
069  /** The sort of array reference types. See {@link #getSort}. */
070  public static final int ARRAY = 9;
071
072  /** The sort of object reference types. See {@link #getSort}. */
073  public static final int OBJECT = 10;
074
075  /** The sort of method types. See {@link #getSort}. */
076  public static final int METHOD = 11;
077
078  /** The (private) sort of object reference types represented with an internal name. */
079  private static final int INTERNAL = 12;
080
081  /** The descriptors of the primitive types. */
082  private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
083
084  /** The {@code void} type. */
085  public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);
086
087  /** The {@code boolean} type. */
088  public static final Type BOOLEAN_TYPE =
089      new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1);
090
091  /** The {@code char} type. */
092  public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1);
093
094  /** The {@code byte} type. */
095  public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1);
096
097  /** The {@code short} type. */
098  public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1);
099
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 true} if 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}