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