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 net.bytebuddy.jar.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 */
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 }
896