001: /* TypeSignature Copyright (C) 1999-2002 Jochen Hoenicke.
002: *
003: * This program is free software; you can redistribute it and/or modify
004: * it under the terms of the GNU Lesser General Public License as published by
005: * the Free Software Foundation; either version 2, or (at your option)
006: * any later version.
007: *
008: * This program is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
011: * GNU General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public License
014: * along with this program; see the file COPYING.LESSER. If not, write to
015: * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
016: *
017: * $Id: TypeSignature.java,v 4.5.2.1 2002/05/28 17:34:01 hoenicke Exp $
018: */
019:
020: package jode.bytecode;
021:
022: import jode.AssertError;
023:
024: /**
025: * This class contains some static methods to handle type signatures.
026: */
027: public class TypeSignature {
028: /**
029: * This is a private method for generating the signature of a
030: * given type.
031: */
032: private static final StringBuffer appendSignature(StringBuffer sb,
033: Class javaType) {
034: if (javaType.isPrimitive()) {
035: if (javaType == Boolean.TYPE)
036: return sb.append('Z');
037: else if (javaType == Byte.TYPE)
038: return sb.append('B');
039: else if (javaType == Character.TYPE)
040: return sb.append('C');
041: else if (javaType == Short.TYPE)
042: return sb.append('S');
043: else if (javaType == Integer.TYPE)
044: return sb.append('I');
045: else if (javaType == Long.TYPE)
046: return sb.append('J');
047: else if (javaType == Float.TYPE)
048: return sb.append('F');
049: else if (javaType == Double.TYPE)
050: return sb.append('D');
051: else if (javaType == Void.TYPE)
052: return sb.append('V');
053: else
054: throw new AssertError("Unknown primitive type: "
055: + javaType);
056: } else if (javaType.isArray()) {
057: return appendSignature(sb.append('['), javaType
058: .getComponentType());
059: } else {
060: return sb.append('L').append(
061: javaType.getName().replace('.', '/')).append(';');
062: }
063: }
064:
065: /**
066: * Generate the signature for the given Class.
067: * @param clazz a java.lang.Class, this may also be a primitive or
068: * array type.
069: * @return the type signature (see section 4.3.2 Field Descriptors
070: * of the JVM specification)
071: */
072: public static String getSignature(Class clazz) {
073: return appendSignature(new StringBuffer(), clazz).toString();
074: }
075:
076: /**
077: * Generate a method signature.
078: * @param paramT the java.lang.Class of the parameter types of the method.
079: * @param returnT the java.lang.Class of the return type of the method.
080: * @return the method signature (see section 4.3.3 Method Descriptors
081: * of the JVM specification)
082: */
083: public static String getSignature(Class paramT[], Class returnT) {
084: StringBuffer sig = new StringBuffer("(");
085: for (int i = 0; i < paramT.length; i++)
086: appendSignature(sig, paramT[i]);
087: return appendSignature(sig.append(')'), returnT).toString();
088: }
089:
090: /**
091: * Generate a Class for a type signature. This is the pendant to
092: * getSignature.
093: * @param typeSig a single type signature
094: * @return the Class object representing that type.
095: */
096: public static Class getClass(String typeSig)
097: throws ClassNotFoundException {
098: switch (typeSig.charAt(0)) {
099: case 'Z':
100: return Boolean.TYPE;
101: case 'B':
102: return Byte.TYPE;
103: case 'C':
104: return Character.TYPE;
105: case 'S':
106: return Short.TYPE;
107: case 'I':
108: return Integer.TYPE;
109: case 'F':
110: return Float.TYPE;
111: case 'J':
112: return Long.TYPE;
113: case 'D':
114: return Double.TYPE;
115: case 'V':
116: return Void.TYPE;
117: case 'L':
118: typeSig = typeSig.substring(1, typeSig.length() - 1)
119: .replace('/', '.');
120: /* fall through */
121: case '[':
122: return Class.forName(typeSig);
123: }
124: throw new IllegalArgumentException(typeSig);
125: }
126:
127: /**
128: * Check if the given type is a two slot type. */
129: private static boolean usingTwoSlots(char type) {
130: return "JD".indexOf(type) >= 0;
131: }
132:
133: /**
134: * Returns the number of words, an object of the given simple type
135: * signature takes.
136: */
137: public static int getTypeSize(String typeSig) {
138: return usingTwoSlots(typeSig.charAt(0)) ? 2 : 1;
139: }
140:
141: public static String getElementType(String typeSig) {
142: if (typeSig.charAt(0) != '[')
143: throw new IllegalArgumentException();
144: return typeSig.substring(1);
145: }
146:
147: public static ClassInfo getClassInfo(String typeSig) {
148: if (typeSig.charAt(0) != 'L')
149: throw new IllegalArgumentException();
150: return ClassInfo.forName(typeSig.substring(1,
151: typeSig.length() - 1).replace('/', '.'));
152: }
153:
154: public static int skipType(String methodTypeSig, int position) {
155: char c = methodTypeSig.charAt(position++);
156: while (c == '[')
157: c = methodTypeSig.charAt(position++);
158: if (c == 'L')
159: return methodTypeSig.indexOf(';', position) + 1;
160: return position;
161: }
162:
163: /**
164: * Returns the number of words, the arguments for the given method
165: * type signature takes.
166: */
167: public static int getArgumentSize(String methodTypeSig) {
168: int nargs = 0;
169: int i = 1;
170: for (;;) {
171: char c = methodTypeSig.charAt(i);
172: if (c == ')')
173: return nargs;
174: i = skipType(methodTypeSig, i);
175: if (usingTwoSlots(c))
176: nargs += 2;
177: else
178: nargs++;
179: }
180: }
181:
182: /**
183: * Returns the number of words, an object of the given simple type
184: * signature takes.
185: */
186: public static int getReturnSize(String methodTypeSig) {
187: int length = methodTypeSig.length();
188: if (methodTypeSig.charAt(length - 2) == ')') {
189: // This is a single character return type.
190: char returnType = methodTypeSig.charAt(length - 1);
191: return returnType == 'V' ? 0
192: : usingTwoSlots(returnType) ? 2 : 1;
193: } else
194: // All multi character return types take one parameter
195: return 1;
196: }
197:
198: /**
199: * Returns the number of words, an object of the given simple type
200: * signature takes.
201: */
202: public static String[] getParameterTypes(String methodTypeSig) {
203: int pos = 1;
204: int count = 0;
205: while (methodTypeSig.charAt(pos) != ')') {
206: pos = skipType(methodTypeSig, pos);
207: count++;
208: }
209: String[] params = new String[count];
210: pos = 1;
211: for (int i = 0; i < count; i++) {
212: int start = pos;
213: pos = skipType(methodTypeSig, pos);
214: params[i] = methodTypeSig.substring(start, pos);
215: }
216: return params;
217: }
218:
219: /**
220: * Returns the number of words, an object of the given simple type
221: * signature takes.
222: */
223: public static String getReturnType(String methodTypeSig) {
224: return methodTypeSig
225: .substring(methodTypeSig.lastIndexOf(')') + 1);
226: }
227:
228: /**
229: * Check if there is a valid class name starting at index
230: * in string typesig and ending with a semicolon.
231: * @return the index at which the class name ends.
232: * @exception IllegalArgumentException if there was an illegal character.
233: * @exception StringIndexOutOfBoundsException if the typesig ended early.
234: */
235: private static int checkClassName(String clName, int i)
236: throws IllegalArgumentException,
237: StringIndexOutOfBoundsException {
238: while (true) {
239: char c = clName.charAt(i++);
240: if (c == ';')
241: return i;
242: if (c != '/' && !Character.isJavaIdentifierPart(c))
243: throw new IllegalArgumentException(
244: "Illegal java class name: " + clName);
245: }
246: }
247:
248: /**
249: * Check if there is a valid simple type signature starting at index
250: * in string typesig.
251: * @return the index at which the type signature ends.
252: * @exception IllegalArgumentException if there was an illegal character.
253: * @exception StringIndexOutOfBoundsException if the typesig ended early.
254: */
255: private static int checkTypeSig(String typesig, int index) {
256: char c = typesig.charAt(index++);
257: while (c == '[')
258: c = typesig.charAt(index++);
259: if (c == 'L') {
260: index = checkClassName(typesig, index);
261: } else {
262: if ("ZBSCIJFD".indexOf(c) == -1)
263: throw new IllegalArgumentException("Type sig error: "
264: + typesig);
265: }
266: return index;
267: }
268:
269: public static void checkTypeSig(String typesig)
270: throws IllegalArgumentException {
271: try {
272: if (checkTypeSig(typesig, 0) != typesig.length())
273: throw new IllegalArgumentException(
274: "Type sig too long: " + typesig);
275: } catch (StringIndexOutOfBoundsException ex) {
276: throw new IllegalArgumentException("Incomplete type sig: "
277: + typesig);
278: }
279: }
280:
281: public static void checkMethodTypeSig(String typesig)
282: throws IllegalArgumentException {
283: try {
284: if (typesig.charAt(0) != '(')
285: throw new IllegalArgumentException(
286: "No method signature: " + typesig);
287: int i = 1;
288: while (typesig.charAt(i) != ')')
289: i = checkTypeSig(typesig, i);
290: // skip closing parenthesis.
291: i++;
292: if (typesig.charAt(i) == 'V')
293: // accept void return type.
294: i++;
295: else
296: i = checkTypeSig(typesig, i);
297: if (i != typesig.length())
298: throw new IllegalArgumentException(
299: "Type sig too long: " + typesig);
300: } catch (StringIndexOutOfBoundsException ex) {
301: throw new IllegalArgumentException("Incomplete type sig: "
302: + typesig);
303: }
304: }
305: }
|