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