From 0552a7af397ffc796c513a3fd06ea76d977d7173 Mon Sep 17 00:00:00 2001 From: chiba Date: Tue, 6 Feb 2007 16:05:29 +0000 Subject: [PATCH] supported generic type signatures. git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@347 30ef5769-5b8d-40dd-aea6-55b5d6557bb3 --- .../javassist/bytecode/ClassFileWriter.java | 32 +- src/main/javassist/bytecode/Descriptor.java | 2 +- .../bytecode/SignatureAttribute.java | 647 ++++++++++++++++++ 3 files changed, 675 insertions(+), 6 deletions(-) diff --git a/src/main/javassist/bytecode/ClassFileWriter.java b/src/main/javassist/bytecode/ClassFileWriter.java index e78a2cd2..3ed2d571 100644 --- a/src/main/javassist/bytecode/ClassFileWriter.java +++ b/src/main/javassist/bytecode/ClassFileWriter.java @@ -68,7 +68,7 @@ public class ClassFileWriter { out.println(Modifier.toString(AccessFlag.toModifier(acc)) + " " + finfo.getName() + "\t" + finfo.getDescriptor()); - printAttributes(finfo.getAttributes(), out); + printAttributes(finfo.getAttributes(), out, false); } out.println(); @@ -80,15 +80,15 @@ public class ClassFileWriter { out.println(Modifier.toString(AccessFlag.toModifier(acc)) + " " + minfo.getName() + "\t" + minfo.getDescriptor()); - printAttributes(minfo.getAttributes(), out); + printAttributes(minfo.getAttributes(), out, false); out.println(); } out.println(); - printAttributes(cf.getAttributes(), out); + printAttributes(cf.getAttributes(), out, true); } - static void printAttributes(List list, PrintWriter out) { + static void printAttributes(List list, PrintWriter out, boolean forClass) { if (list == null) return; @@ -104,9 +104,31 @@ public class ClassFileWriter { + ", " + ca.getExceptionTable().size() + " catch blocks"); out.println(""); - printAttributes(ca.getAttributes(), out); + printAttributes(ca.getAttributes(), out, forClass); out.println(""); } + else if (ai instanceof StackMapTable) { + out.println(""); + StackMapTable.Writer.print((StackMapTable)ai, out); + out.println(""); + } + else if (ai instanceof SignatureAttribute) { + SignatureAttribute sa = (SignatureAttribute)ai; + String sig = sa.getSignature(); + out.println("signature: " + sig); + try { + String s; + if (forClass) + s = SignatureAttribute.toClassSignature(sig).toString(); + else + s = SignatureAttribute.toMethodSignature(sig).toString(); + + out.println(" " + s); + } + catch (BadBytecode e) { + out.println(" syntax error"); + } + } else out.println("attribute: " + ai.getName() + " (" + ai.get().length + " byte): " diff --git a/src/main/javassist/bytecode/Descriptor.java b/src/main/javassist/bytecode/Descriptor.java index f7185ab7..60b6cff5 100644 --- a/src/main/javassist/bytecode/Descriptor.java +++ b/src/main/javassist/bytecode/Descriptor.java @@ -550,7 +550,7 @@ public class Descriptor { return i2; } - private static CtClass toPrimitiveClass(char c) { + static CtClass toPrimitiveClass(char c) { CtClass type = null; switch (c) { case 'Z' : diff --git a/src/main/javassist/bytecode/SignatureAttribute.java b/src/main/javassist/bytecode/SignatureAttribute.java index c6fa3cfb..39f4e35b 100644 --- a/src/main/javassist/bytecode/SignatureAttribute.java +++ b/src/main/javassist/bytecode/SignatureAttribute.java @@ -18,6 +18,8 @@ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; +import java.util.ArrayList; +import javassist.CtClass; /** * Signature_attribute. @@ -51,6 +53,9 @@ public class SignatureAttribute extends AttributeInfo { /** * Returns the signature indicated by signature_index. + * + * @see #toClassSignature(String) + * @see #toMethodSignature(String) */ public String getSignature() { return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); @@ -67,4 +72,646 @@ public class SignatureAttribute extends AttributeInfo { public AttributeInfo copy(ConstPool newCp, Map classnames) { return new SignatureAttribute(newCp, getSignature()); } + + static private class Cursor { + int position = 0; + + int indexOf(String s, int ch) throws BadBytecode { + int i = s.indexOf(ch, position); + if (i < 0) + throw error(s); + else { + position = i + 1; + return i; + } + } + } + + /** + * Class signature. + */ + public static class ClassSignature { + TypeParameter[] params; + ClassType superClass; + ClassType[] interfaces; + ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) { + params = p; + superClass = s; + interfaces = i; + } + + /** + * Returns the type parameters. + * + * @return a zero-length array if the type parameters are not specified. + */ + public TypeParameter[] getParameters() { + return params; + } + + /** + * Returns the super class. + */ + public ClassType getSuperClass() { return superClass; } + + /** + * Returns the super interfaces. + * + * @return a zero-length array if the super interfaces are not specified. + */ + public ClassType[] getInterfaces() { return interfaces; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + + TypeParameter.toString(sbuf, params); + sbuf.append(" extends ").append(superClass); + if (interfaces.length > 0) { + sbuf.append(" implements "); + Type.toString(sbuf, interfaces); + } + + return sbuf.toString(); + } + } + + /** + * Method type signature. + */ + public static class MethodSignature { + TypeParameter[] typeParams; + Type[] params; + Type retType; + ObjectType[] exceptions; + + MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) { + typeParams = tp; + params = p; + retType = ret; + exceptions = ex; + } + + /** + * Returns the formal type parameters. + * + * @return a zero-length array if the type parameters are not specified. + */ + public TypeParameter[] getTypeParameters() { return typeParams; } + + /** + * Returns the types of the formal parameters. + * + * @return a zero-length array if no formal parameter is taken. + */ + public Type[] getParameterTypes() { return params; } + + /** + * Returns the type of the returned value. + */ + public Type getReturnType() { return retType; } + + /** + * Returns the types of the exceptions that may be thrown. + * + * @return a zero-length array if exceptions are never thrown or + * the exception types are not parameterized types or type variables. + */ + public ObjectType[] getExceptionTypes() { return exceptions; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + + TypeParameter.toString(sbuf, typeParams); + sbuf.append(" ("); + Type.toString(sbuf, params); + sbuf.append(") "); + sbuf.append(retType); + if (exceptions.length > 0) { + sbuf.append(" throws "); + Type.toString(sbuf, exceptions); + } + + return sbuf.toString(); + } + } + + /** + * Formal type parameters. + */ + public static class TypeParameter { + String name; + ObjectType superClass; + ObjectType[] superInterfaces; + + TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) { + name = sig.substring(nb, ne); + superClass = sc; + superInterfaces = si; + } + + /** + * Returns the name of the type parameter. + */ + public String getName() { + return name; + } + + /** + * Returns the class bound of this parameter. + * + * @return null if the class bound is not specified. + */ + public ObjectType getClassBound() { return superClass; } + + /** + * Returns the interface bound of this parameter. + * + * @return a zero-length array if the interface bound is not specified. + */ + public ObjectType[] getInterfaceBound() { return superInterfaces; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(getName()); + if (superClass != null) + sbuf.append(" extends ").append(superClass.toString()); + + int len = superInterfaces.length; + if (len > 0) { + for (int i = 0; i < len; i++) { + if (i > 0 || superClass != null) + sbuf.append(" & "); + else + sbuf.append(" extends "); + + sbuf.append(superInterfaces[i].toString()); + } + } + + return sbuf.toString(); + } + + static void toString(StringBuffer sbuf, TypeParameter[] tp) { + sbuf.append('<'); + for (int i = 0; i < tp.length; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(tp[i]); + } + + sbuf.append('>'); + } + } + + /** + * Type argument. + */ + public static class TypeArgument { + ObjectType arg; + char wildcard; + + TypeArgument(ObjectType a, char w) { + arg = a; + wildcard = w; + } + + /** + * Returns the kind of this type argument. + * + * @return ' ' (not-wildcard), '*' (wildcard), '+' (wildcard with + * upper bound), or '-' (wildcard with lower bound). + */ + public char getKind() { return wildcard; } + + /** + * Returns true if this type argument is a wildcard type such as + * ?, ? extends String, or ? super Integer. + */ + public boolean isWildcard() { return wildcard != ' '; } + + /** + * Returns the type represented by this argument + * if the argument is not a wildcard type. Otherwise, this method + * returns the upper bound (if the kind is '+'), + * the lower bound (if the kind is '-'), or null (if the upper or lower + * bound is not specified). + */ + public ObjectType getType() { return arg; } + + /** + * Returns the string representation. + */ + public String toString() { + if (wildcard == '*') + return "?"; + + String type = arg.toString(); + if (wildcard == ' ') + return type; + else if (wildcard == '+') + return "? extends " + type; + else + return "? super " + type; + } + } + + /** + * Primitive types and object types. + */ + public static abstract class Type { + static void toString(StringBuffer sbuf, Type[] ts) { + for (int i = 0; i < ts.length; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(ts[i]); + } + } + } + + /** + * Primitive types. + */ + public static class BaseType extends Type { + char descriptor; + BaseType(char c) { descriptor = c; } + + /** + * Returns the descriptor representing this primitive type. + * + * @see javassist.bytecode.Descriptor + */ + public char getDescriptor() { return descriptor; } + + /** + * Returns the CtClass representing this + * primitive type. + */ + public CtClass getCtlass() { + return Descriptor.toPrimitiveClass(descriptor); + } + + /** + * Returns the string representation. + */ + public String toString() { + return Descriptor.toClassName(Character.toString(descriptor)); + } + } + + /** + * Class types, array types, and type variables. + */ + public static abstract class ObjectType extends Type {} + + /** + * Class types. + */ + public static class ClassType extends ObjectType { + String name; + TypeArgument[] arguments; + + static ClassType make(String s, int b, int e, + TypeArgument[] targs, ClassType parent) { + if (parent == null) + return new ClassType(s, b, e, targs); + else + return new NestedClassType(s, b, e, targs, parent); + } + + ClassType(String signature, int begin, int end, TypeArgument[] targs) { + name = signature.substring(begin, end).replace('/', '.'); + arguments = targs; + } + + /** + * Returns the class name. + */ + public String getName() { + return name; + } + + /** + * Returns the type arguments. + * + * @return null if no type arguments are given to this class. + */ + public TypeArgument[] getTypeArguments() { return arguments; } + + /** + * If this class is a member of another class, returns the + * class in which this class is declared. + * + * @return null if this class is not a member of another class. + */ + public ClassType getDeclaringClass() { return null; } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(); + ClassType parent = getDeclaringClass(); + if (parent != null) + sbuf.append(parent.toString()).append('.'); + + sbuf.append(name); + if (arguments != null) { + sbuf.append('<'); + int n = arguments.length; + for (int i = 0; i < n; i++) { + if (i > 0) + sbuf.append(", "); + + sbuf.append(arguments[i].toString()); + } + + sbuf.append('>'); + } + + return sbuf.toString(); + } + } + + static class NestedClassType extends ClassType { + ClassType parent; + NestedClassType(String s, int b, int e, + TypeArgument[] targs, ClassType p) { + super(s, b, e, targs); + parent = p; + } + + public ClassType getDeclaringClass() { return parent; } + } + + /** + * Array types. + */ + public static class ArrayType extends ObjectType { + int dim; + Type componentType; + + public ArrayType(int d, Type comp) { + dim = d; + componentType = comp; + } + + /** + * Returns the dimension of the array. + */ + public int getDimension() { return dim; } + + /** + * Returns the component type. + */ + public Type getComponentType() { + return componentType; + } + + /** + * Returns the string representation. + */ + public String toString() { + StringBuffer sbuf = new StringBuffer(componentType.toString()); + for (int i = 0; i < dim; i++) + sbuf.append("[]"); + + return sbuf.toString(); + } + } + + public static class TypeVariable extends ObjectType { + String name; + + TypeVariable(String sig, int begin, int end) { + name = sig.substring(begin, end); + } + + /** + * Returns the variable name. + */ + public String getName() { + return name; + } + + /** + * Returns the string representation. + */ + public String toString() { + return name; + } + } + + /** + * Parses the given signature string as a class signature. + * + * @param sig the signature. + * @throws BadBytecode thrown when a syntactical error is found. + */ + public static ClassSignature toClassSignature(String sig) throws BadBytecode { + try { + return parseSig(sig); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + /** + * Parses the given signature string as a method type signature. + * + * @param sig the signature. + * @throws BadBytecode thrown when a syntactical error is found. + */ + public static MethodSignature toMethodSignature(String sig) throws BadBytecode { + try { + return parseMethodSig(sig); + } + catch (IndexOutOfBoundsException e) { + throw error(sig); + } + } + + private static ClassSignature parseSig(String sig) + throws BadBytecode, IndexOutOfBoundsException + { + Cursor cur = new Cursor(); + TypeParameter[] tp = parseTypeParams(sig, cur); + ClassType superClass = parseClassType(sig, cur); + int sigLen = sig.length(); + ArrayList ifArray = new ArrayList(); + while (cur.position < sigLen && sig.charAt(cur.position) == 'L') + ifArray.add(parseClassType(sig, cur)); + + ClassType[] ifs + = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]); + return new ClassSignature(tp, superClass, ifs); + } + + private static MethodSignature parseMethodSig(String sig) + throws BadBytecode + { + Cursor cur = new Cursor(); + TypeParameter[] tp = parseTypeParams(sig, cur); + if (sig.charAt(cur.position++) != '(') + throw error(sig); + + ArrayList params = new ArrayList(); + while (sig.charAt(cur.position) != ')') { + Type t = parseType(sig, cur); + params.add(t); + } + + cur.position++; + Type ret = parseType(sig, cur); + int sigLen = sig.length(); + ArrayList exceptions = new ArrayList(); + while (cur.position < sigLen && sig.charAt(cur.position) == '^') { + cur.position++; + ObjectType t = parseObjectType(sig, cur, false); + if (t instanceof ArrayType) + throw error(sig); + + exceptions.add(t); + } + + Type[] p = (Type[])params.toArray(new Type[params.size()]); + ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]); + return new MethodSignature(tp, p, ret, ex); + } + + private static TypeParameter[] parseTypeParams(String sig, Cursor cur) + throws BadBytecode + { + ArrayList typeParam = new ArrayList(); + if (sig.charAt(cur.position) == '<') { + cur.position++; + while (sig.charAt(cur.position) != '>') { + int nameBegin = cur.position; + int nameEnd = cur.indexOf(sig, ':'); + ObjectType classBound = parseObjectType(sig, cur, true); + ArrayList ifBound = new ArrayList(); + while (sig.charAt(cur.position) == ':') { + cur.position++; + ObjectType t = parseObjectType(sig, cur, false); + ifBound.add(t); + } + + TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd, + classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()])); + typeParam.add(p); + } + + cur.position++; + } + + return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]); + } + + private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow) + throws BadBytecode + { + int i; + int begin = c.position; + switch (sig.charAt(begin)) { + case 'L' : + return parseClassType2(sig, c, null); + case 'T' : + i = c.indexOf(sig, ';'); + return new TypeVariable(sig, begin + 1, i); + case '[' : + return parseArray(sig, c); + default : + if (dontThrow) + return null; + else + throw error(sig); + } + } + + private static ClassType parseClassType(String sig, Cursor c) + throws BadBytecode + { + if (sig.charAt(c.position) == 'L') + return parseClassType2(sig, c, null); + else + throw error(sig); + } + + private static ClassType parseClassType2(String sig, Cursor c, ClassType parent) + throws BadBytecode + { + int start = ++c.position; + char t; + do { + t = sig.charAt(c.position++); + } while (t != '$' && t != '<' && t != ';'); + int end = c.position - 1; + TypeArgument[] targs; + if (t == '<') { + targs = parseTypeArgs(sig, c); + t = sig.charAt(c.position++); + } + else + targs = null; + + ClassType thisClass = ClassType.make(sig, start, end, targs, parent); + if (t == '$') { + c.position--; + return parseClassType2(sig, c, thisClass); + } + else + return thisClass; + } + + private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode { + ArrayList args = new ArrayList(); + char t; + while ((t = sig.charAt(c.position++)) != '>') { + TypeArgument ta; + if (t == '*' ) + ta = new TypeArgument(null, '*'); + else { + if (t != '+' && t != '-') { + t = ' '; + c.position--; + } + + ta = new TypeArgument(parseObjectType(sig, c, false), t); + } + + args.add(ta); + } + + return (TypeArgument[])args.toArray(new TypeArgument[args.size()]); + } + + private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode { + int dim = 1; + while (sig.charAt(++c.position) == '[') + dim++; + + return new ArrayType(dim, parseType(sig, c)); + } + + private static Type parseType(String sig, Cursor c) throws BadBytecode { + Type t = parseObjectType(sig, c, true); + if (t == null) + t = new BaseType(sig.charAt(c.position++)); + + return t; + } + + private static BadBytecode error(String sig) { + return new BadBytecode("bad signature: " + sig); + } } -- 2.39.5