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