1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2005 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 package org.objectweb.asm;
31 
32 import java.lang.reflect.Method;
33 
34 /**
35  * A Java type. This class can be used to make it easier to manipulate type and
36  * method descriptors.
37  *
38  * @author Eric Bruneton
39  * @author Chris Nokleberg
40  */
41 public class Type {
42 
43     /**
44      * The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
45      */
46     public final static int VOID = 0;
47 
48     /**
49      * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
50      */
51     public final static int BOOLEAN = 1;
52 
53     /**
54      * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
55      */
56     public final static int CHAR = 2;
57 
58     /**
59      * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
60      */
61     public final static int BYTE = 3;
62 
63     /**
64      * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
65      */
66     public final static int SHORT = 4;
67 
68     /**
69      * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
70      */
71     public final static int INT = 5;
72 
73     /**
74      * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
75      */
76     public final static int FLOAT = 6;
77 
78     /**
79      * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
80      */
81     public final static int LONG = 7;
82 
83     /**
84      * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
85      */
86     public final static int DOUBLE = 8;
87 
88     /**
89      * The sort of array reference types. See {@link #getSort getSort}.
90      */
91     public final static int ARRAY = 9;
92 
93     /**
94      * The sort of object reference type. See {@link #getSort getSort}.
95      */
96     public final static int OBJECT = 10;
97 
98     /**
99      * The <tt>void</tt> type.
100      */
101     public final static Type VOID_TYPE = new Type(VOID);
102 
103     /**
104      * The <tt>boolean</tt> type.
105      */
106     public final static Type BOOLEAN_TYPE = new Type(BOOLEAN);
107 
108     /**
109      * The <tt>char</tt> type.
110      */
111     public final static Type CHAR_TYPE = new Type(CHAR);
112 
113     /**
114      * The <tt>byte</tt> type.
115      */
116     public final static Type BYTE_TYPE = new Type(BYTE);
117 
118     /**
119      * The <tt>short</tt> type.
120      */
121     public final static Type SHORT_TYPE = new Type(SHORT);
122 
123     /**
124      * The <tt>int</tt> type.
125      */
126     public final static Type INT_TYPE = new Type(INT);
127 
128     /**
129      * The <tt>float</tt> type.
130      */
131     public final static Type FLOAT_TYPE = new Type(FLOAT);
132 
133     /**
134      * The <tt>long</tt> type.
135      */
136     public final static Type LONG_TYPE = new Type(LONG);
137 
138     /**
139      * The <tt>double</tt> type.
140      */
141     public final static Type DOUBLE_TYPE = new Type(DOUBLE);
142 
143     // ------------------------------------------------------------------------
144     // Fields
145     // ------------------------------------------------------------------------
146 
147     /**
148      * The sort of this Java type.
149      */
150     private final int sort;
151 
152     /**
153      * A buffer containing the descriptor of this Java type. This field is only
154      * used for reference types.
155      */
156     private char[] buf;
157 
158     /**
159      * The offset of the descriptor of this Java type in {@link #buf buf}. This
160      * field is only used for reference types.
161      */
162     private int off;
163 
164     /**
165      * The length of the descriptor of this Java type.
166      */
167     private int len;
168 
169     // ------------------------------------------------------------------------
170     // Constructors
171     // ------------------------------------------------------------------------
172 
173     /**
174      * Constructs a primitive type.
175      *
176      * @param sort the sort of the primitive type to be constructed.
177      */
Type(final int sort)178     private Type(final int sort) {
179         this.sort = sort;
180         this.len = 1;
181     }
182 
183     /**
184      * Constructs a reference type.
185      *
186      * @param sort the sort of the reference type to be constructed.
187      * @param buf a buffer containing the descriptor of the previous type.
188      * @param off the offset of this descriptor in the previous buffer.
189      * @param len the length of this descriptor.
190      */
Type(final int sort, final char[] buf, final int off, final int len)191     private Type(final int sort, final char[] buf, final int off, final int len)
192     {
193         this.sort = sort;
194         this.buf = buf;
195         this.off = off;
196         this.len = len;
197     }
198 
199     /**
200      * Returns the Java type corresponding to the given type descriptor.
201      *
202      * @param typeDescriptor a type descriptor.
203      * @return the Java type corresponding to the given type descriptor.
204      */
getType(final String typeDescriptor)205     public static Type getType(final String typeDescriptor) {
206         return getType(typeDescriptor.toCharArray(), 0);
207     }
208 
209     /**
210      * Returns the Java type corresponding to the given class.
211      *
212      * @param c a class.
213      * @return the Java type corresponding to the given class.
214      */
getType(final Class c)215     public static Type getType(final Class c) {
216         if (c.isPrimitive()) {
217             if (c == Integer.TYPE) {
218                 return INT_TYPE;
219             } else if (c == Void.TYPE) {
220                 return VOID_TYPE;
221             } else if (c == Boolean.TYPE) {
222                 return BOOLEAN_TYPE;
223             } else if (c == Byte.TYPE) {
224                 return BYTE_TYPE;
225             } else if (c == Character.TYPE) {
226                 return CHAR_TYPE;
227             } else if (c == Short.TYPE) {
228                 return SHORT_TYPE;
229             } else if (c == Double.TYPE) {
230                 return DOUBLE_TYPE;
231             } else if (c == Float.TYPE) {
232                 return FLOAT_TYPE;
233             } else /* if (c == Long.TYPE) */{
234                 return LONG_TYPE;
235             }
236         } else {
237             return getType(getDescriptor(c));
238         }
239     }
240 
241     /**
242      * Returns the Java types corresponding to the argument types of the given
243      * method descriptor.
244      *
245      * @param methodDescriptor a method descriptor.
246      * @return the Java types corresponding to the argument types of the given
247      *         method descriptor.
248      */
getArgumentTypes(final String methodDescriptor)249     public static Type[] getArgumentTypes(final String methodDescriptor) {
250         char[] buf = methodDescriptor.toCharArray();
251         int off = 1;
252         int size = 0;
253         while (true) {
254             char car = buf[off++];
255             if (car == ')') {
256                 break;
257             } else if (car == 'L') {
258                 while (buf[off++] != ';') {
259                 }
260                 ++size;
261             } else if (car != '[') {
262                 ++size;
263             }
264         }
265         Type[] args = new Type[size];
266         off = 1;
267         size = 0;
268         while (buf[off] != ')') {
269             args[size] = getType(buf, off);
270             off += args[size].len;
271             size += 1;
272         }
273         return args;
274     }
275 
276     /**
277      * Returns the Java types corresponding to the argument types of the given
278      * method.
279      *
280      * @param method a method.
281      * @return the Java types corresponding to the argument types of the given
282      *         method.
283      */
getArgumentTypes(final Method method)284     public static Type[] getArgumentTypes(final Method method) {
285         Class[] classes = method.getParameterTypes();
286         Type[] types = new Type[classes.length];
287         for (int i = classes.length - 1; i >= 0; --i) {
288             types[i] = getType(classes[i]);
289         }
290         return types;
291     }
292 
293     /**
294      * Returns the Java type corresponding to the return type of the given
295      * method descriptor.
296      *
297      * @param methodDescriptor a method descriptor.
298      * @return the Java type corresponding to the return type of the given
299      *         method descriptor.
300      */
getReturnType(final String methodDescriptor)301     public static Type getReturnType(final String methodDescriptor) {
302         char[] buf = methodDescriptor.toCharArray();
303         return getType(buf, methodDescriptor.indexOf(')') + 1);
304     }
305 
306     /**
307      * Returns the Java type corresponding to the return type of the given
308      * method.
309      *
310      * @param method a method.
311      * @return the Java type corresponding to the return type of the given
312      *         method.
313      */
getReturnType(final Method method)314     public static Type getReturnType(final Method method) {
315         return getType(method.getReturnType());
316     }
317 
318     /**
319      * Returns the Java type corresponding to the given type descriptor.
320      *
321      * @param buf a buffer containing a type descriptor.
322      * @param off the offset of this descriptor in the previous buffer.
323      * @return the Java type corresponding to the given type descriptor.
324      */
getType(final char[] buf, final int off)325     private static Type getType(final char[] buf, final int off) {
326         int len;
327         switch (buf[off]) {
328             case 'V':
329                 return VOID_TYPE;
330             case 'Z':
331                 return BOOLEAN_TYPE;
332             case 'C':
333                 return CHAR_TYPE;
334             case 'B':
335                 return BYTE_TYPE;
336             case 'S':
337                 return SHORT_TYPE;
338             case 'I':
339                 return INT_TYPE;
340             case 'F':
341                 return FLOAT_TYPE;
342             case 'J':
343                 return LONG_TYPE;
344             case 'D':
345                 return DOUBLE_TYPE;
346             case '[':
347                 len = 1;
348                 while (buf[off + len] == '[') {
349                     ++len;
350                 }
351                 if (buf[off + len] == 'L') {
352                     ++len;
353                     while (buf[off + len] != ';') {
354                         ++len;
355                     }
356                 }
357                 return new Type(ARRAY, buf, off, len + 1);
358             // case 'L':
359             default:
360                 len = 1;
361                 while (buf[off + len] != ';') {
362                     ++len;
363                 }
364                 return new Type(OBJECT, buf, off, len + 1);
365         }
366     }
367 
368     // ------------------------------------------------------------------------
369     // Accessors
370     // ------------------------------------------------------------------------
371 
372     /**
373      * Returns the sort of this Java type.
374      *
375      * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN},
376      *         {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT},
377      *         {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG},
378      *         {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or
379      *         {@link #OBJECT OBJECT}.
380      */
getSort()381     public int getSort() {
382         return sort;
383     }
384 
385     /**
386      * Returns the number of dimensions of this array type. This method should
387      * only be used for an array type.
388      *
389      * @return the number of dimensions of this array type.
390      */
getDimensions()391     public int getDimensions() {
392         int i = 1;
393         while (buf[off + i] == '[') {
394             ++i;
395         }
396         return i;
397     }
398 
399     /**
400      * Returns the type of the elements of this array type. This method should
401      * only be used for an array type.
402      *
403      * @return Returns the type of the elements of this array type.
404      */
getElementType()405     public Type getElementType() {
406         return getType(buf, off + getDimensions());
407     }
408 
409     /**
410      * Returns the name of the class corresponding to this type.
411      *
412      * @return the fully qualified name of the class corresponding to this type.
413      */
getClassName()414     public String getClassName() {
415         switch (sort) {
416             case VOID:
417                 return "void";
418             case BOOLEAN:
419                 return "boolean";
420             case CHAR:
421                 return "char";
422             case BYTE:
423                 return "byte";
424             case SHORT:
425                 return "short";
426             case INT:
427                 return "int";
428             case FLOAT:
429                 return "float";
430             case LONG:
431                 return "long";
432             case DOUBLE:
433                 return "double";
434             case ARRAY:
435                 StringBuffer b = new StringBuffer(getElementType().getClassName());
436                 for (int i = getDimensions(); i > 0; --i) {
437                     b.append("[]");
438                 }
439                 return b.toString();
440             // case OBJECT:
441             default:
442                 return new String(buf, off + 1, len - 2).replace('/', '.');
443         }
444     }
445 
446     /**
447      * Returns the internal name of the class corresponding to this object type.
448      * The internal name of a class is its fully qualified name, where '.' are
449      * replaced by '/'. This method should only be used for an object type.
450      *
451      * @return the internal name of the class corresponding to this object type.
452      */
getInternalName()453     public String getInternalName() {
454         return new String(buf, off + 1, len - 2);
455     }
456 
457     // ------------------------------------------------------------------------
458     // Conversion to type descriptors
459     // ------------------------------------------------------------------------
460 
461     /**
462      * Returns the descriptor corresponding to this Java type.
463      *
464      * @return the descriptor corresponding to this Java type.
465      */
getDescriptor()466     public String getDescriptor() {
467         StringBuffer buf = new StringBuffer();
468         getDescriptor(buf);
469         return buf.toString();
470     }
471 
472     /**
473      * Returns the descriptor corresponding to the given argument and return
474      * types.
475      *
476      * @param returnType the return type of the method.
477      * @param argumentTypes the argument types of the method.
478      * @return the descriptor corresponding to the given argument and return
479      *         types.
480      */
getMethodDescriptor( final Type returnType, final Type[] argumentTypes)481     public static String getMethodDescriptor(
482         final Type returnType,
483         final Type[] argumentTypes)
484     {
485         StringBuffer buf = new StringBuffer();
486         buf.append('(');
487         for (int i = 0; i < argumentTypes.length; ++i) {
488             argumentTypes[i].getDescriptor(buf);
489         }
490         buf.append(')');
491         returnType.getDescriptor(buf);
492         return buf.toString();
493     }
494 
495     /**
496      * Appends the descriptor corresponding to this Java type to the given
497      * string buffer.
498      *
499      * @param buf the string buffer to which the descriptor must be appended.
500      */
getDescriptor(final StringBuffer buf)501     private void getDescriptor(final StringBuffer buf) {
502         switch (sort) {
503             case VOID:
504                 buf.append('V');
505                 return;
506             case BOOLEAN:
507                 buf.append('Z');
508                 return;
509             case CHAR:
510                 buf.append('C');
511                 return;
512             case BYTE:
513                 buf.append('B');
514                 return;
515             case SHORT:
516                 buf.append('S');
517                 return;
518             case INT:
519                 buf.append('I');
520                 return;
521             case FLOAT:
522                 buf.append('F');
523                 return;
524             case LONG:
525                 buf.append('J');
526                 return;
527             case DOUBLE:
528                 buf.append('D');
529                 return;
530             // case ARRAY:
531             // case OBJECT:
532             default:
533                 buf.append(this.buf, off, len);
534         }
535     }
536 
537     // ------------------------------------------------------------------------
538     // Direct conversion from classes to type descriptors,
539     // without intermediate Type objects
540     // ------------------------------------------------------------------------
541 
542     /**
543      * Returns the internal name of the given class. The internal name of a
544      * class is its fully qualified name, where '.' are replaced by '/'.
545      *
546      * @param c an object class.
547      * @return the internal name of the given class.
548      */
getInternalName(final Class c)549     public static String getInternalName(final Class c) {
550         return c.getName().replace('.', '/');
551     }
552 
553     /**
554      * Returns the descriptor corresponding to the given Java type.
555      *
556      * @param c an object class, a primitive class or an array class.
557      * @return the descriptor corresponding to the given class.
558      */
getDescriptor(final Class c)559     public static String getDescriptor(final Class c) {
560         StringBuffer buf = new StringBuffer();
561         getDescriptor(buf, c);
562         return buf.toString();
563     }
564 
565     /**
566      * Returns the descriptor corresponding to the given method.
567      *
568      * @param m a {@link Method Method} object.
569      * @return the descriptor of the given method.
570      */
getMethodDescriptor(final Method m)571     public static String getMethodDescriptor(final Method m) {
572         Class[] parameters = m.getParameterTypes();
573         StringBuffer buf = new StringBuffer();
574         buf.append('(');
575         for (int i = 0; i < parameters.length; ++i) {
576             getDescriptor(buf, parameters[i]);
577         }
578         buf.append(')');
579         getDescriptor(buf, m.getReturnType());
580         return buf.toString();
581     }
582 
583     /**
584      * Appends the descriptor of the given class to the given string buffer.
585      *
586      * @param buf the string buffer to which the descriptor must be appended.
587      * @param c the class whose descriptor must be computed.
588      */
getDescriptor(final StringBuffer buf, final Class c)589     private static void getDescriptor(final StringBuffer buf, final Class c) {
590         Class d = c;
591         while (true) {
592             if (d.isPrimitive()) {
593                 char car;
594                 if (d == Integer.TYPE) {
595                     car = 'I';
596                 } else if (d == Void.TYPE) {
597                     car = 'V';
598                 } else if (d == Boolean.TYPE) {
599                     car = 'Z';
600                 } else if (d == Byte.TYPE) {
601                     car = 'B';
602                 } else if (d == Character.TYPE) {
603                     car = 'C';
604                 } else if (d == Short.TYPE) {
605                     car = 'S';
606                 } else if (d == Double.TYPE) {
607                     car = 'D';
608                 } else if (d == Float.TYPE) {
609                     car = 'F';
610                 } else /* if (d == Long.TYPE) */{
611                     car = 'J';
612                 }
613                 buf.append(car);
614                 return;
615             } else if (d.isArray()) {
616                 buf.append('[');
617                 d = d.getComponentType();
618             } else {
619                 buf.append('L');
620                 String name = d.getName();
621                 int len = name.length();
622                 for (int i = 0; i < len; ++i) {
623                     char car = name.charAt(i);
624                     buf.append(car == '.' ? '/' : car);
625                 }
626                 buf.append(';');
627                 return;
628             }
629         }
630     }
631 
632     // ------------------------------------------------------------------------
633     // Corresponding size and opcodes
634     // ------------------------------------------------------------------------
635 
636     /**
637      * Returns the size of values of this type.
638      *
639      * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
640      *         <tt>double</tt>, and 1 otherwise.
641      */
getSize()642     public int getSize() {
643         return (sort == LONG || sort == DOUBLE ? 2 : 1);
644     }
645 
646     /**
647      * Returns a JVM instruction opcode adapted to this Java type.
648      *
649      * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD,
650      *        ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL,
651      *        ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
652      * @return an opcode that is similar to the given opcode, but adapted to
653      *         this Java type. For example, if this type is <tt>float</tt> and
654      *         <tt>opcode</tt> is IRETURN, this method returns FRETURN.
655      */
getOpcode(final int opcode)656     public int getOpcode(final int opcode) {
657         if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
658             switch (sort) {
659                 case BOOLEAN:
660                 case BYTE:
661                     return opcode + 5;
662                 case CHAR:
663                     return opcode + 6;
664                 case SHORT:
665                     return opcode + 7;
666                 case INT:
667                     return opcode;
668                 case FLOAT:
669                     return opcode + 2;
670                 case LONG:
671                     return opcode + 1;
672                 case DOUBLE:
673                     return opcode + 3;
674                 // case ARRAY:
675                 // case OBJECT:
676                 default:
677                     return opcode + 4;
678             }
679         } else {
680             switch (sort) {
681                 case VOID:
682                     return opcode + 5;
683                 case BOOLEAN:
684                 case CHAR:
685                 case BYTE:
686                 case SHORT:
687                 case INT:
688                     return opcode;
689                 case FLOAT:
690                     return opcode + 2;
691                 case LONG:
692                     return opcode + 1;
693                 case DOUBLE:
694                     return opcode + 3;
695                 // case ARRAY:
696                 // case OBJECT:
697                 default:
698                     return opcode + 4;
699             }
700         }
701     }
702 
703     // ------------------------------------------------------------------------
704     // Equals, hashCode and toString
705     // ------------------------------------------------------------------------
706 
707     /**
708      * Tests if the given object is equal to this type.
709      *
710      * @param o the object to be compared to this type.
711      * @return <tt>true</tt> if the given object is equal to this type.
712      */
equals(final Object o)713     public boolean equals(final Object o) {
714         if (this == o) {
715             return true;
716         }
717         if (o == null || !(o instanceof Type)) {
718             return false;
719         }
720         Type t = (Type) o;
721         if (sort != t.sort) {
722             return false;
723         }
724         if (sort == Type.OBJECT || sort == Type.ARRAY) {
725             if (len != t.len) {
726                 return false;
727             }
728             for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
729                 if (buf[i] != t.buf[j]) {
730                     return false;
731                 }
732             }
733         }
734         return true;
735     }
736 
737     /**
738      * Returns a hash code value for this type.
739      *
740      * @return a hash code value for this type.
741      */
hashCode()742     public int hashCode() {
743         int hc = 13 * sort;
744         if (sort == Type.OBJECT || sort == Type.ARRAY) {
745             for (int i = off, end = i + len; i < end; i++) {
746                 hc = 17 * (hc + buf[i]);
747             }
748         }
749         return hc;
750     }
751 
752     /**
753      * Returns a string representation of this type.
754      *
755      * @return the descriptor of this type.
756      */
toString()757     public String toString() {
758         return getDescriptor();
759     }
760 }
761