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