diff options
Diffstat (limited to 'src/main/javassist/bytecode/Descriptor.java')
-rw-r--r-- | src/main/javassist/bytecode/Descriptor.java | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java new file mode 100644 index 00000000..065fbccf --- /dev/null +++ b/src/main/javassist/bytecode/Descriptor.java @@ -0,0 +1,543 @@ +/* + * This file is part of the Javassist toolkit. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.bytecode; + +import java.util.Map; +import javassist.CtClass; +import javassist.CtPrimitiveType; +import javassist.ClassPool; +import javassist.NotFoundException; + +/** + * A support class for dealing with descriptors. + * + * <p>See chapter 4.3 in "The Java Virtual Machine Specification (2nd ed.)" + */ +public class Descriptor { + /** + * Converts a class name into the internal representation used in + * the JVM. + * + * <p>Note that <code>toJvmName(toJvmName(s))</code> is equivalent + * to <code>toJvmName(s)</code>. + */ + public static String toJvmName(String classname) { + return classname.replace('.', '/'); + } + + /** + * Converts a class name from the internal representation used in + * the JVM to the normal one used in Java. + */ + public static String toJavaName(String classname) { + return classname.replace('/', '.'); + } + + /** + * Returns the internal representation of the class name in the + * JVM. + */ + public static String toJvmName(CtClass clazz) { + if (clazz.isArray()) + return of(clazz); + else + return toJvmName(clazz.getName()); + } + + /** + * Substitutes a class name + * in the given descriptor string. + * + * @param desc descriptor string + * @param oldname replaced JVM class name + * @param newname substituted JVM class name + * + * @see Descriptor#toJvmName(String) + */ + public static String rename(String desc, + String oldname, String newname) { + if (desc.indexOf(oldname) < 0) + return desc; + + StringBuffer newdesc = new StringBuffer(); + int head = 0; + int i = 0; + for (;;) { + int j = desc.indexOf('L', i); + if (j < 0) + break; + else if (desc.startsWith(oldname, j + 1) + && desc.charAt(j + oldname.length() + 1) == ';') { + newdesc.append(desc.substring(head, j)); + newdesc.append('L'); + newdesc.append(newname); + newdesc.append(';'); + head = i = j + oldname.length() + 2; + } + else { + i = desc.indexOf(';', j) + 1; + if (i < 1) + break; // ';' was not found. + } + } + + if (head == 0) + return desc; + else { + int len = desc.length(); + if (head < len) + newdesc.append(desc.substring(head, len)); + + return newdesc.toString(); + } + } + + /** + * Substitutes class names in the given descriptor string + * according to the given <code>map</code>. + * + * @param map a map between replaced and substituted + * JVM class names. + * + * @see Descriptor#toJvmName(String) + */ + public static String rename(String desc, Map map) { + if (map == null) + return desc; + + StringBuffer newdesc = new StringBuffer(); + int head = 0; + int i = 0; + for (;;) { + int j = desc.indexOf('L', i); + if (j < 0) + break; + + int k = desc.indexOf(';', j); + if (k < 0) + break; + + i = k + 1; + String name = desc.substring(j + 1, k); + String name2 = (String)map.get(name); + if (name2 != null) { + newdesc.append(desc.substring(head, j)); + newdesc.append('L'); + newdesc.append(name2); + newdesc.append(';'); + head = i; + } + } + + if (head == 0) + return desc; + else { + int len = desc.length(); + if (head < len) + newdesc.append(desc.substring(head, len)); + + return newdesc.toString(); + } + } + + /** + * Returns the descriptor representing the given type. + */ + public static String of(CtClass type) { + StringBuffer sbuf = new StringBuffer(); + toDescriptor(sbuf, type); + return sbuf.toString(); + } + + private static void toDescriptor(StringBuffer desc, CtClass type) { + if (type.isArray()) { + desc.append('['); + try { + toDescriptor(desc, type.getComponentType()); + } + catch (NotFoundException e) { + desc.append('L'); + String name = type.getName(); + desc.append(toJvmName(name.substring(0, name.length() - 2))); + desc.append(';'); + } + } + else if (type.isPrimitive()) { + CtPrimitiveType pt = (CtPrimitiveType)type; + desc.append(pt.getDescriptor()); + } + else { // class type + desc.append('L'); + desc.append(type.getName().replace('.', '/')); + desc.append(';'); + } + } + + /** + * Returns the descriptor representing a constructor receiving + * the given parameter types. + * + * @param paramTypes parameter types + */ + public static String ofConstructor(CtClass[] paramTypes) { + return ofMethod(CtClass.voidType, paramTypes); + } + + /** + * Returns the descriptor representing a method that receives + * the given parameter types and returns the given type. + * + * @param returnType return type + * @param paramTypes parameter types + */ + public static String ofMethod(CtClass returnType, CtClass[] paramTypes) { + StringBuffer desc = new StringBuffer(); + desc.append('('); + if (paramTypes != null) { + int n = paramTypes.length; + for (int i = 0; i < n; ++i) + toDescriptor(desc, paramTypes[i]); + } + + desc.append(')'); + if (returnType != null) + toDescriptor(desc, returnType); + + return desc.toString(); + } + + /** + * Returns the descriptor representing a list of parameter types. + * For example, if the given parameter types are two <code>int</code>, + * then this method returns <code>"(II)"</code>. + * + * @param paramTypes parameter types + */ + public static String ofParameters(CtClass[] paramTypes) { + return ofMethod(null, paramTypes); + } + + /** + * Appends a parameter type to the parameter list represented + * by the given descriptor. + * + * <p><code>classname</code> must not be an array type. + * + * @param classname parameter type (not primitive type) + * @param desc descriptor + */ + public static String appendParameter(String classname, + String desc) { + int i = desc.indexOf(')'); + if (i < 0) + return desc; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(desc.substring(0, i)); + newdesc.append('L'); + newdesc.append(classname.replace('.', '/')); + newdesc.append(';'); + newdesc.append(desc.substring(i)); + return newdesc.toString(); + } + } + + /** + * Inserts a parameter type at the beginning of the parameter + * list represented + * by the given descriptor. + * + * <p><code>classname</code> must not be an array type. + * + * @param classname parameter type (not primitive type) + * @param desc descriptor + */ + public static String insertParameter(String classname, + String desc) { + if (desc.charAt(0) != '(') + return desc; + else + return "(L" + classname.replace('.', '/') + ';' + + desc.substring(1); + } + + /** + * Changes the return type included in the given descriptor. + * + * <p><code>classname</code> must not be an array type. + * + * @param classname return type + * @param desc descriptor + */ + public static String changeReturnType(String classname, String desc) { + int i = desc.indexOf(')'); + if (i < 0) + return desc; + else { + StringBuffer newdesc = new StringBuffer(); + newdesc.append(desc.substring(0, i + 1)); + newdesc.append('L'); + newdesc.append(classname.replace('.', '/')); + newdesc.append(';'); + return newdesc.toString(); + } + } + + /** + * Returns the <code>CtClass</code> objects representing the parameter + * types specified by the given descriptor. + * + * @param desc descriptor + * @param cp the class pool used for obtaining + * a <code>CtClass</code> object. + */ + public static CtClass[] getParameterTypes(String desc, ClassPool cp) + throws NotFoundException + { + if (desc.charAt(0) != '(') + return null; + else { + int num = numOfParameters(desc); + CtClass[] args = new CtClass[num]; + int n = 0; + int i = 1; + do { + i = toCtClass(cp, desc, i, args, n++); + } while(i > 0); + return args; + } + } + + /** + * Returns the <code>CtClass</code> object representing the return + * type specified by the given descriptor. + * + * @param desc descriptor + * @param cp the class pool used for obtaining + * a <code>CtClass</code> object. + */ + public static CtClass getReturnType(String desc, ClassPool cp) + throws NotFoundException + { + int i = desc.indexOf(')'); + if (i < 0) + return null; + else { + CtClass[] type = new CtClass[1]; + toCtClass(cp, desc, i + 1, type, 0); + return type[0]; + } + } + + /** + * Returns the number of the prameters included in the given + * descriptor. + * + * @param desc descriptor + */ + public static int numOfParameters(String desc) { + int n = 0; + int i = 1; + for (;;) { + char c = desc.charAt(i); + if (c == ')') + break; + + while (c == '[') + c = desc.charAt(++i); + + if (c == 'L') { + i = desc.indexOf(';', i) + 1; + if (i <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++i; + + ++n; + } + + return n; + } + + /** + * Returns a <code>CtClass</code> object representing the type + * specified by the given descriptor. + * + * <p>This method works even if the package-class separator is + * not <code>/</code> but <code>.</code> (period). For example, + * it accepts <code>Ljava.lang.Object;</code> + * as well as <code>Ljava/lang/Object;</code>. + * + * @param desc descriptor + * @param cp the class pool used for obtaining + * a <code>CtClass</code> object. + */ + public static CtClass toCtClass(String desc, ClassPool cp) + throws NotFoundException + { + CtClass[] clazz = new CtClass[1]; + int res = toCtClass(cp, desc, 0, clazz, 0); + if (res >= 0) + return clazz[0]; + else { + // maybe, you forgot to surround the class name with + // L and ;. It violates the protocol, but I'm tolerant... + return cp.get(desc.replace('/', '.')); + } + } + + private static int toCtClass(ClassPool cp, String desc, int i, + CtClass[] args, int n) + throws NotFoundException + { + int i2; + String name; + + int arrayDim = 0; + char c = desc.charAt(i); + while (c == '[') { + ++arrayDim; + c = desc.charAt(++i); + } + + if (c == 'L') { + i2 = desc.indexOf(';', ++i); + name = desc.substring(i, i2++).replace('/', '.'); + } + else { + CtClass type = toPrimitiveClass(c); + if (type == null) + return -1; // error + + i2 = i + 1; + if (arrayDim == 0) { + args[n] = type; + return i2; // neither an array type or a class type + } + else + name = type.getName(); + } + + if (arrayDim > 0) { + StringBuffer sbuf = new StringBuffer(name); + while (arrayDim-- > 0) + sbuf.append("[]"); + + name = sbuf.toString(); + } + + args[n] = cp.get(name); + return i2; + } + + private static CtClass toPrimitiveClass(char c) { + CtClass type = null; + switch (c) { + case 'Z' : + type = CtClass.booleanType; + break; + case 'C' : + type = CtClass.charType; + break; + case 'B' : + type = CtClass.byteType; + break; + case 'S' : + type = CtClass.shortType; + break; + case 'I' : + type = CtClass.intType; + break; + case 'J' : + type = CtClass.longType; + break; + case 'F' : + type = CtClass.floatType; + break; + case 'D' : + type = CtClass.doubleType; + break; + case 'V' : + type = CtClass.voidType; + break; + } + + return type; + } + + /** + * Computes the data size specified by the given descriptor. + * For example, if the descriptor is "D", this method returns 2. + * + * <p>If the descriptor represents a method type, this method returns + * (the size of the returned value) - (the sum of the data sizes + * of all the parameters). For example, if the descriptor is + * "(I)D", then this method returns 1 (= 2 - 1). + * + * @param desc descriptor + */ + public static int dataSize(String desc) { + int n = 0; + char c = desc.charAt(0); + if (c == '(') { + int i = 1; + for (;;) { + c = desc.charAt(i); + if (c == ')') { + c = desc.charAt(i + 1); + break; + } + + boolean array = false; + while (c == '[') { + array = true; + c = desc.charAt(++i); + } + + if (c == 'L') { + i = desc.indexOf(';', i) + 1; + if (i <= 0) + throw new IndexOutOfBoundsException("bad descriptor"); + } + else + ++i; + + if (!array && (c == 'J' || c == 'D')) + n -= 2; + else + --n; + } + } + + if (c == 'J' || c == 'D') + n += 2; + else if (c != 'V') + ++n; + + return n; + } +} |