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