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 io.ebean.enhance.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 (see {@link Type#getInternalName()}).
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 = 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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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
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
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
917  public String toString() {
918    return getDescriptor();
919  }
920}