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