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 */
30package com.sleepycat.asm;
31
32import 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 */
41public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
757    public String toString() {
758        return getDescriptor();
759    }
760}
761