1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm; 29 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.Method; 32 33 /** 34 * A Java field or method type. This class can be used to make it easier to manipulate type and 35 * method descriptors. 36 * 37 * @author Eric Bruneton 38 * @author Chris Nokleberg 39 */ 40 public final class Type { 41 42 /** The sort of the {@code void} type. See {@link #getSort}. */ 43 public static final int VOID = 0; 44 45 /** The sort of the {@code boolean} type. See {@link #getSort}. */ 46 public static final int BOOLEAN = 1; 47 48 /** The sort of the {@code char} type. See {@link #getSort}. */ 49 public static final int CHAR = 2; 50 51 /** The sort of the {@code byte} type. See {@link #getSort}. */ 52 public static final int BYTE = 3; 53 54 /** The sort of the {@code short} type. See {@link #getSort}. */ 55 public static final int SHORT = 4; 56 57 /** The sort of the {@code int} type. See {@link #getSort}. */ 58 public static final int INT = 5; 59 60 /** The sort of the {@code float} type. See {@link #getSort}. */ 61 public static final int FLOAT = 6; 62 63 /** The sort of the {@code long} type. See {@link #getSort}. */ 64 public static final int LONG = 7; 65 66 /** The sort of the {@code double} type. See {@link #getSort}. */ 67 public static final int DOUBLE = 8; 68 69 /** The sort of array reference types. See {@link #getSort}. */ 70 public static final int ARRAY = 9; 71 72 /** The sort of object reference types. See {@link #getSort}. */ 73 public static final int OBJECT = 10; 74 75 /** The sort of method types. See {@link #getSort}. */ 76 public static final int METHOD = 11; 77 78 /** The (private) sort of object reference types represented with an internal name. */ 79 private static final int INTERNAL = 12; 80 81 /** The descriptors of the primitive types. */ 82 private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD"; 83 84 /** The {@code void} type. */ 85 public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1); 86 87 /** The {@code boolean} type. */ 88 public static final Type BOOLEAN_TYPE = 89 new Type(BOOLEAN, PRIMITIVE_DESCRIPTORS, BOOLEAN, BOOLEAN + 1); 90 91 /** The {@code char} type. */ 92 public static final Type CHAR_TYPE = new Type(CHAR, PRIMITIVE_DESCRIPTORS, CHAR, CHAR + 1); 93 94 /** The {@code byte} type. */ 95 public static final Type BYTE_TYPE = new Type(BYTE, PRIMITIVE_DESCRIPTORS, BYTE, BYTE + 1); 96 97 /** The {@code short} type. */ 98 public static final Type SHORT_TYPE = new Type(SHORT, PRIMITIVE_DESCRIPTORS, SHORT, SHORT + 1); 99 100 /** The {@code int} type. */ 101 public static final Type INT_TYPE = new Type(INT, PRIMITIVE_DESCRIPTORS, INT, INT + 1); 102 103 /** The {@code float} type. */ 104 public static final Type FLOAT_TYPE = new Type(FLOAT, PRIMITIVE_DESCRIPTORS, FLOAT, FLOAT + 1); 105 106 /** The {@code long} type. */ 107 public static final Type LONG_TYPE = new Type(LONG, PRIMITIVE_DESCRIPTORS, LONG, LONG + 1); 108 109 /** The {@code double} type. */ 110 public static final Type DOUBLE_TYPE = 111 new Type(DOUBLE, PRIMITIVE_DESCRIPTORS, DOUBLE, DOUBLE + 1); 112 113 // ----------------------------------------------------------------------------------------------- 114 // Fields 115 // ----------------------------------------------------------------------------------------------- 116 117 /** 118 * The sort of this type. Either {@link #VOID}, {@link #BOOLEAN}, {@link #CHAR}, {@link #BYTE}, 119 * {@link #SHORT}, {@link #INT}, {@link #FLOAT}, {@link #LONG}, {@link #DOUBLE}, {@link #ARRAY}, 120 * {@link #OBJECT}, {@link #METHOD} or {@link #INTERNAL}. 121 */ 122 private final int sort; 123 124 /** 125 * A buffer containing the value of this field or method type. This value is an internal name for 126 * {@link #OBJECT} and {@link #INTERNAL} types, and a field or method descriptor in the other 127 * cases. 128 * 129 * <p>For {@link #OBJECT} types, this field also contains the descriptor: the characters in 130 * [{@link #valueBegin},{@link #valueEnd}) contain the internal name, and those in [{@link 131 * #valueBegin} - 1, {@link #valueEnd} + 1) contain the descriptor. 132 */ 133 private final String valueBuffer; 134 135 /** 136 * The beginning index, inclusive, of the value of this Java field or method type in {@link 137 * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, 138 * and a field or method descriptor in the other cases. 139 */ 140 private final int valueBegin; 141 142 /** 143 * The end index, exclusive, of the value of this Java field or method type in {@link 144 * #valueBuffer}. This value is an internal name for {@link #OBJECT} and {@link #INTERNAL} types, 145 * and a field or method descriptor in the other cases. 146 */ 147 private final int valueEnd; 148 149 /** 150 * Constructs a reference type. 151 * 152 * @param sort the sort of this type, see {@link #sort}. 153 * @param valueBuffer a buffer containing the value of this field or method type. 154 * @param valueBegin the beginning index, inclusive, of the value of this field or method type in 155 * valueBuffer. 156 * @param valueEnd the end index, exclusive, of the value of this field or method type in 157 * valueBuffer. 158 */ Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd)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 */ getType(final String typeDescriptor)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 */ getType(final Class<?> clazz)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 */ getType(final Constructor<?> constructor)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 */ getType(final Method method)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 */ getElementType()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 */ getObjectType(final String internalName)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 */ getMethodType(final String methodDescriptor)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 */ getMethodType(final Type returnType, final Type... argumentTypes)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 */ getArgumentTypes()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 */ getArgumentTypes(final String methodDescriptor)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 */ getArgumentTypes(final Method method)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 */ getReturnType()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 */ getReturnType(final String methodDescriptor)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 */ getReturnType(final Method method)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 */ getReturnTypeOffset(final String methodDescriptor)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 */ getTypeInternal( final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd)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 */ getClassName()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 */ getInternalName()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 */ getInternalName(final Class<?> clazz)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 */ getDescriptor()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 */ getDescriptor(final Class<?> clazz)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 */ getConstructorDescriptor(final Constructor<?> constructor)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 */ getMethodDescriptor(final Type returnType, final Type... argumentTypes)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 */ getMethodDescriptor(final Method method)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 */ appendDescriptor(final StringBuilder stringBuilder)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 */ appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder)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 */ getSort()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 */ getDimensions()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 */ getSize()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 */ getArgumentCount()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 */ getArgumentCount(final String methodDescriptor)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 << 2) | returnSize} (argumentsSize is therefore equal to {@code 735 * i >> 2}, and returnSize to {@code i & 0x03}). Long and double values have size 2, 736 * the others have size 1. 737 */ getArgumentsAndReturnSizes()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 << 2) | returnSize} (argumentsSize is therefore equal to {@code 749 * i >> 2}, and returnSize to {@code i & 0x03}). Long and double values have size 2, 750 * the others have size 1. 751 */ getArgumentsAndReturnSizes(final String methodDescriptor)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 */ getOpcode(final int opcode)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 equals(final Object object)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 hashCode()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 toString()917 public String toString() { 918 return getDescriptor(); 919 } 920 } 921