]> source.dussan.org Git - javassist.git/commitdiff
implemented JASSIST-170
authorchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Sun, 17 Jun 2012 15:01:27 +0000 (15:01 +0000)
committerchiba <chiba@30ef5769-5b8d-40dd-aea6-55b5d6557bb3>
Sun, 17 Jun 2012 15:01:27 +0000 (15:01 +0000)
git-svn-id: http://anonsvn.jboss.org/repos/javassist/trunk@639 30ef5769-5b8d-40dd-aea6-55b5d6557bb3

Readme.html
src/main/javassist/CtBehavior.java
src/main/javassist/CtClass.java
src/main/javassist/CtClassType.java
src/main/javassist/CtField.java
src/main/javassist/CtMember.java
src/main/javassist/bytecode/SignatureAttribute.java
src/test/javassist/JvstTest4.java
tutorial/tutorial.html
tutorial/tutorial3.html

index ef74f1711ec927cf9e90f68c98dd26391ae4e194..0d2922389b8ba9794b50abcd9503a606e5a31808 100644 (file)
@@ -284,7 +284,7 @@ see javassist.Dump.
 <p>-version 3.17
 <ul>
        <li>OSGi bundle info is now included in the jar file.
-       <li>JIRA JASSIST-166, 168 have been fixed.
+       <li>JIRA JASSIST-166, 168, 170 have been fixed.
 </ul>
 
 <p>-version 3.16.1 on March 6, 2012
index e01f868c78a1344fa49056595bc60d656fa3926e..06873e2c0f4e73beaaebc196ed2e76f5c6ce6f84 100644 (file)
@@ -314,19 +314,43 @@ public abstract class CtBehavior extends CtMember {
      *
      * <p>Note that the returned string is not the type signature
      * contained in the <code>SignatureAttirbute</code>.  It is
-     * a descriptor.  To obtain a type signature, call the following
-     * methods:
-     * 
-     * <ul><pre>getMethodInfo().getAttribute(SignatureAttribute.tag)
-     * </pre></ul>
+     * a descriptor.
      *
      * @see javassist.bytecode.Descriptor
-     * @see javassist.bytecode.SignatureAttribute
+     * @see #getGenericSignature()
      */
     public String getSignature() {
         return methodInfo.getDescriptor();
     }
 
+    /**
+     * Returns the generic signature of the method.
+     * It represents parameter types including type variables.
+     *
+     * @see SignatureAttribute#toMethodSignature(String)
+     * @since 3.17
+     */
+    public String getGenericSignature() {
+        SignatureAttribute sa
+            = (SignatureAttribute)methodInfo.getAttribute(SignatureAttribute.tag);
+        return sa == null ? null : sa.getSignature();
+    }
+
+    /**
+     * Set the generic signature of the method.
+     * It represents parameter types including type variables.
+     * See {@link javassist.CtClass#setGenericSignature(String)}
+     * for a code sample.
+     *
+     * @param sig       a new generic signature.
+     * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+     * @since 3.17
+     */
+    public void setGenericSignature(String sig) {
+        declaringClass.checkModify();
+        methodInfo.addAttribute(new SignatureAttribute(methodInfo.getConstPool(), sig));
+    }
+
     /**
      * Obtains exceptions that this method/constructor may throw.
      *
index 7f115d0dea38191cda8fbbe9d36e0355ff97b18e..bcf61e419eed4ac7147f9efaa830809c78f88cd9 100644 (file)
@@ -29,6 +29,7 @@ import java.util.Collection;
 import javassist.bytecode.ClassFile;
 import javassist.bytecode.Descriptor;
 import javassist.bytecode.Opcode;
+import javassist.bytecode.SignatureAttribute;
 import javassist.expr.ExprEditor;
 
 /* Note:
@@ -387,6 +388,89 @@ public abstract class CtClass {
             qualifiedName = name;
     }
 
+    /**
+     * Returns the generic signature of the class.
+     *
+     * <p>The generics of Java is implemented by the erasure technique.
+     * After compilation, all type parameters are dropped off from the
+     * main part of a class file.  However, for reflection, the type
+     * parameters are encoded into generic signatures and attached
+     * to a class file.
+     *
+     * @return null if the generic signature is not included.
+     * @see javassist.bytecode.SignatureAttribute#toClassSignature(String)
+     * @see CtMember#getGenericSignature()
+     * @since 3.17
+     */
+    public String getGenericSignature() { return null; }
+
+    /**
+     * Sets the generic signature of the class.
+     *
+     * <p>The generics of Java is implemented by the erasure technique.
+     * After compilation, all type parameters are dropped off from the
+     * main part of a class file.  However, for reflection, the type
+     * parameters must be encoded into generic signatures and attached
+     * to a class file.
+     *
+     * <p>For example,
+     *
+     * <pre>class List<T> {
+     *     T value;
+     *     T get() { return value; }
+     *     void set(T v) { value = v; }
+     * }
+     * </pre>
+     *
+     * <p>this class is generated by the following code:
+     *
+     * <pre>
+     * ClassPool pool = ClassPool.getDefault();
+     * CtClass cc = pool.makeClass("List");
+     * CtClass objectClass = pool.get(CtClass.javaLangObject);
+     * ClassSignature cs = new ClassSignature(
+     *                         new TypeParameter[] { new TypeParameter("T") });
+     * cc.setGenericSignature(cs.encode());    // &lt;T:Ljava/lang/Object;&gt;Ljava/lang/Object;
+     *
+     * CtField f = new CtField(objClass, "value", cc);
+     * TypeVariable tvar = new TypeVariable("T");
+     * f.setGenericSignature(tvar.encode());   // TT;
+     * cc.addField(f);
+     *
+     * CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
+     * MethodSignature ms = new MethodSignature(null, null, tvar, null);
+     * m.setGenericSignature(ms.encode());     // ()TT;
+     * cc.addMethod(m);
+     *
+     * CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
+     * MethodSignature ms2 = new MethodSignature(null, new Type[] { tvar },
+     *                                           new BaseType("void"), null);
+     * m2.setGenericSignature(ms2.encode());   // (TT;)V;
+     * cc.addMethod(m2);
+     *
+     * cc.writeFile();
+     * </pre>
+     *
+     * <p>The generated class file is equivalent to the following:
+     *
+     * <pre>class List {
+     *     Object value;
+     *     Object get() { return value; }
+     *     void set(Object v) { value = v; }
+     * }</pre>
+     *
+     * <p>but it includes generic signatures for the class, the field,
+     * and the methods so that the type variable <code>T</code> can be
+     * accessible through reflection.
+     *
+     * @param sig       a generic signature.
+     * @see javassist.bytecode.SignatureAttribute.ClassSignature#encode()
+     * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+     * @see CtMember#setGenericSignature(String)
+     * @since 3.17
+     */
+    public void setGenericSignature(String sig) { checkModify(); }
+
     /**
      * Substitutes <code>newName</code> for all occurrences of a class
      * name <code>oldName</code> in the class file.
index abbc25fa4c73f60335995bf077f8fdb5d3dcf3e3..4d10b5dde7d60c4a1d536a509924da70da00dcbf 100644 (file)
@@ -47,6 +47,7 @@ import javassist.bytecode.FieldInfo;
 import javassist.bytecode.InnerClassesAttribute;
 import javassist.bytecode.MethodInfo;
 import javassist.bytecode.ParameterAnnotationsAttribute;
+import javassist.bytecode.SignatureAttribute;
 import javassist.bytecode.annotation.Annotation;
 import javassist.compiler.AccessorMaker;
 import javassist.compiler.CompileError;
@@ -336,6 +337,18 @@ class CtClassType extends CtClass {
         classPool.classNameChanged(oldname, this);
     }
 
+    public String getGenericSignature() {
+        SignatureAttribute sa
+            = (SignatureAttribute)getClassFile2().getAttribute(SignatureAttribute.tag);
+        return sa == null ? null : sa.getSignature();
+    }
+
+    public void setGenericSignature(String sig) {
+        ClassFile cf = getClassFile();
+        SignatureAttribute sa = new SignatureAttribute(cf.getConstPool(), sig);
+        cf.addAttribute(sa);
+    }
+
     public void replaceClassName(ClassMap classnames)
         throws RuntimeException
     {
index a16e1396611e310dccf9664c10d29d096409be38..16e450bfe05da05079321ac07609d4c9d39fe131 100644 (file)
@@ -327,19 +327,43 @@ public class CtField extends CtMember {
      *
      * <p>Note that the returned string is not the type signature
      * contained in the <code>SignatureAttirbute</code>.  It is
-     * a descriptor.  To obtain a type signature, call the following
-     * methods:
-     * 
-     * <ul><pre>getFieldInfo().getAttribute(SignatureAttribute.tag)
-     * </pre></ul>
+     * a descriptor.
      *
      * @see javassist.bytecode.Descriptor
-     * @see javassist.bytecode.SignatureAttribute
+     * @see #getGenericSignature()
      */
     public String getSignature() {
         return fieldInfo.getDescriptor();
     }
 
+    /**
+     * Returns the generic signature of the field.
+     * It represents a type including type variables.
+     *
+     * @see SignatureAttribute#toFieldSignature(String)
+     * @since 3.17
+     */
+    public String getGenericSignature() {
+        SignatureAttribute sa
+            = (SignatureAttribute)fieldInfo.getAttribute(SignatureAttribute.tag);
+        return sa == null ? null : sa.getSignature();
+    }
+
+    /**
+     * Set the generic signature of the field.
+     * It represents a type including type variables.
+     * See {@link javassist.CtClass#setGenericSignature(String)}
+     * for a code sample.
+     *
+     * @param sig       a new generic signature.
+     * @see javassist.bytecode.SignatureAttribute.ObjectType#encode()
+     * @since 3.17
+     */
+    public void setGenericSignature(String sig) {
+        declaringClass.checkModify();
+        fieldInfo.addAttribute(new SignatureAttribute(fieldInfo.getConstPool(), sig));
+    }
+
     /**
      * Returns the type of the field.
      */
index e75130d5ba8f75af235dc8f93a5403b591d9850d..ae141c65489c24ab9c798579c4380acea19f5ec3 100644 (file)
@@ -42,6 +42,8 @@ public abstract class CtMember {
         public String getSignature() { return null; }
         public void setAttribute(String name, byte[] data) {}
         public void setModifiers(int mod) {}
+        public String getGenericSignature() { return null; }
+        public void setGenericSignature(String sig) {}
 
         private CtMember methodTail;
         private CtMember consTail;     // constructor tail
@@ -269,6 +271,27 @@ public abstract class CtMember {
      */
     public abstract String getSignature();
 
+    /**
+     * Returns the generic signature of the member.
+     *
+     * @see javassist.bytecode.SignatureAttribute#toFieldSignature(String)
+     * @see javassist.bytecode.SignatureAttribute#toMethodSignature(String)
+     * @see CtClass#getGenericSignature()
+     * @since 3.17
+     */
+    public abstract String getGenericSignature();
+
+    /**
+     * Sets the generic signature of the member.
+     *
+     * @param sig   a new generic signature.
+     * @see javassist.bytecode.SignatureAttribute.ObjectType#encode()
+     * @see javassist.bytecode.SignatureAttribute.MethodSignature#encode()
+     * @see CtClass#setGenericSignature(String)
+     * @since 3.17
+     */
+    public abstract void setGenericSignature(String sig);
+
     /**
      * Obtains a user-defined attribute with the given name.
      * If that attribute is not found in the class file, this
index 08d7f3b8185d7a7e88d4e6bd659b47d03a98a98a..9e6921c5df422e3d0a8455b3bd5e4f6bd1cd62e7 100644 (file)
@@ -38,7 +38,7 @@ public class SignatureAttribute extends AttributeInfo {
     }
 
     /**
-     * Constructs a Signature attribute.
+     * Constructs a <code>Signature</code> attribute.
      *
      * @param cp                a constant pool table.
      * @param signature         the signature represented by this attribute.
@@ -53,17 +53,18 @@ public class SignatureAttribute extends AttributeInfo {
     }
 
     /**
-     * Returns the signature indicated by <code>signature_index</code>.
+     * Returns the generic signature indicated by <code>signature_index</code>.
      *
      * @see #toClassSignature(String)
      * @see #toMethodSignature(String)
+     * @see #toFieldSignature(String)
      */
     public String getSignature() {
         return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0));
     }
 
     /**
-     * Sets <code>signature_index</code> to the index of the given signature,
+     * Sets <code>signature_index</code> to the index of the given generic signature,
      * which is added to a constant pool.
      *
      * @param sig       new signature.
@@ -177,10 +178,27 @@ public class SignatureAttribute extends AttributeInfo {
         TypeParameter[] params;
         ClassType superClass;
         ClassType[] interfaces;
-        ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) {
-            params = p;
-            superClass = s;
-            interfaces = i;
+
+        /**
+         * Constructs a class signature.
+         *
+         * @param p         type parameters.
+         * @param s         the super class.
+         * @param i         the interface types.
+         */
+        public ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) {
+            params = p == null ? new TypeParameter[0] : p;
+            superClass = s == null ? ClassType.OBJECT : s;
+            interfaces = i == null ? new ClassType[0] : i;
+        }
+
+        /**
+         * Constructs a class signature.
+         *
+         * @param p         type parameters.
+         */
+        public ClassSignature(TypeParameter[] p) {
+            this(p, null, null);
         }
 
         /**
@@ -219,6 +237,26 @@ public class SignatureAttribute extends AttributeInfo {
 
             return sbuf.toString();
         }
+
+        /**
+         * Returns the encoded string representing the method type signature.
+         */
+        public String encode() {
+            StringBuffer sbuf = new StringBuffer();
+            if (params.length > 0) {
+                sbuf.append('<');
+                for (int i = 0; i < params.length; i++)
+                    params[i].encode(sbuf);
+
+                sbuf.append('>');
+            }
+
+            superClass.encode(sbuf);
+            for (int i = 0; i < interfaces.length; i++)
+                interfaces[i].encode(sbuf);
+
+            return sbuf.toString();
+        }
     }
 
     /**
@@ -230,11 +268,20 @@ public class SignatureAttribute extends AttributeInfo {
         Type retType;
         ObjectType[] exceptions;
 
-        MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) {
-            typeParams = tp;
-            params = p;
-            retType = ret;
-            exceptions = ex;
+        /**
+         * Constructs a method type signature.  Any parameter can be null
+         * to represent <code>void</code> or nothing.
+         *
+         * @param tp        type parameters.
+         * @param p         parameter types.
+         * @param ret       a return type.
+         * @param ex        exception types.
+         */
+        public MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) {
+            typeParams = tp == null ? new TypeParameter[0] : tp;
+            params = p == null ? new Type[0] : p;
+            retType = ret == null ? new BaseType("void") : ret;
+            exceptions = ex == null ? new ObjectType[0] : ex;
         }
 
         /**
@@ -282,10 +329,40 @@ public class SignatureAttribute extends AttributeInfo {
 
             return sbuf.toString();
         }
+
+        /**
+         * Returns the encoded string representing the method type signature.
+         */
+        public String encode() {
+            StringBuffer sbuf = new StringBuffer();
+            if (typeParams.length > 0) {
+                sbuf.append('<');
+                for (int i = 0; i < typeParams.length; i++)
+                    typeParams[i].encode(sbuf);
+
+                sbuf.append('>');
+            }
+
+            sbuf.append('(');
+            for (int i = 0; i < params.length; i++)
+                params[i].encode(sbuf);
+
+            sbuf.append(')');
+            retType.encode(sbuf);
+            if (exceptions.length > 0)
+                for (int i = 0; i < exceptions.length; i++) {
+                    sbuf.append('^');
+                    exceptions[i].encode(sbuf);
+                }
+
+            return sbuf.toString();
+        }
     }
 
     /**
      * Formal type parameters.
+     *
+     * @see TypeArgument
      */
     public static class TypeParameter {
         String name;
@@ -298,6 +375,33 @@ public class SignatureAttribute extends AttributeInfo {
             superInterfaces = si;
         }
 
+        /**
+         * Constructs a <code>TypeParameter</code> representing a type parametre
+         * like <code>&lt;T extends ... &gt;<code>.
+         *
+         * @param name      parameter name.
+         * @param superClass    an upper bound class-type (or null).
+         * @param superInterfaces   an upper bound interface-type (or null).
+         */
+        public TypeParameter(String name, ObjectType superClass, ObjectType[] superInterfaces) {
+            this.name = name;
+            this.superClass = superClass;
+            if (superInterfaces == null)
+                this.superInterfaces = new ObjectType[0];
+            else
+                this.superInterfaces = superInterfaces;
+        }
+
+        /**
+         * Constructs a <code>TypeParameter</code> representing a type parameter
+         * like <code>&lt;T&gt;<code>.
+         *
+         * @param name          parameter name.
+         */
+        public TypeParameter(String name) {
+            this(name, null, null);
+        }
+
         /**
          * Returns the name of the type parameter.
          */
@@ -307,8 +411,6 @@ public class SignatureAttribute extends AttributeInfo {
 
         /**
          * Returns the class bound of this parameter.
-         *
-         * @return null if the class bound is not specified.
          */
         public ObjectType getClassBound() { return superClass; }
 
@@ -353,10 +455,27 @@ public class SignatureAttribute extends AttributeInfo {
 
             sbuf.append('>');
         }
+
+        void encode(StringBuffer sb) {
+            sb.append(name);
+            if (superClass == null)
+                sb.append(":Ljava/lang/Object;");
+            else {
+                sb.append(':');
+                superClass.encode(sb);
+            }
+
+            for (int i = 0; i < superInterfaces.length; i++) {
+                sb.append(':');
+                superInterfaces[i].encode(sb);
+            }
+        }
     }
 
     /**
      * Type argument.
+     *
+     * @see TypeParameter
      */
     public static class TypeArgument {
         ObjectType arg;
@@ -367,6 +486,44 @@ public class SignatureAttribute extends AttributeInfo {
             wildcard = w;
         }
 
+        /**
+         * Constructs a <code>TypeArgument</code>.
+         * A type argument is <code>&lt;String&gt;</code>, <code>&lt;int[]&gt;</code>,
+         * or a type variable <code>&lt;T&gt;</code>, etc.
+         *
+         * @param t         a class type, an array type, or a type variable.  
+         */
+        public TypeArgument(ObjectType t) {
+            this(t, ' ');
+        }
+
+        /**
+         * Constructs a <code>TypeArgument</code> representing <code>&lt;?&gt;</code>.
+         */
+        public TypeArgument() {
+            this(null, '*');
+        }
+
+        /**
+         * A factory method constructing a <code>TypeArgument</code> with an upper bound.
+         * It represents <code>&lt;? extends ... &gt;</code> 
+         * 
+         * @param t     an upper bound type.
+         */
+        public static TypeArgument subclassOf(ObjectType t) {
+            return new TypeArgument(t, '+');
+        }
+
+        /**
+         * A factory method constructing a <code>TypeArgument</code> with an lower bound.
+         * It represents <code>&lt;? super ... &gt;</code>
+         * 
+         * @param t     an lower bbound type.
+         */
+        public static TypeArgument superOf(ObjectType t) {
+            return new TypeArgument(t, '-');
+        }
+
         /**
          * Returns the kind of this type argument.
          *
@@ -405,12 +562,27 @@ public class SignatureAttribute extends AttributeInfo {
             else
                 return "? super " + type;
         }
+
+        static void encode(StringBuffer sb, TypeArgument[] args) {
+            sb.append('<');
+            for (int i = 0; i < args.length; i++) {
+                TypeArgument ta = args[i];
+                if (ta.isWildcard())
+                    sb.append(ta.wildcard);
+
+                if (ta.getType() != null)
+                    ta.getType().encode(sb);
+            }
+
+            sb.append('>');
+        }
     }
 
     /**
      * Primitive types and object types.
      */
     public static abstract class Type {
+        abstract void encode(StringBuffer sb);
         static void toString(StringBuffer sbuf, Type[] ts) {
             for (int i = 0; i < ts.length; i++) {
                 if (i > 0)
@@ -428,6 +600,15 @@ public class SignatureAttribute extends AttributeInfo {
         char descriptor;
         BaseType(char c) { descriptor = c; }
 
+        /**
+         * Constructs a <code>BaseType</code>.
+         *
+         * @param typeName      <code>void</code>, <code>int</code>, ... 
+         */
+        public BaseType(String typeName) {
+            this(Descriptor.of(typeName).charAt(0));
+        }
+
         /**
          * Returns the descriptor representing this primitive type.
          *
@@ -449,12 +630,26 @@ public class SignatureAttribute extends AttributeInfo {
         public String toString() {
             return Descriptor.toClassName(Character.toString(descriptor));
         }
+
+        void encode(StringBuffer sb) {
+            sb.append(descriptor);
+        }
     }
 
     /**
      * Class types, array types, and type variables.
+     * This class is also used for representing a field type.
      */
-    public static abstract class ObjectType extends Type {}
+    public static abstract class ObjectType extends Type {
+        /**
+         * Returns the encoded string representing the object type signature.
+         */
+        public String encode() {
+            StringBuffer sb = new StringBuffer();
+            encode(sb);
+            return sb.toString();
+        }
+    }
 
     /**
      * Class types.
@@ -476,6 +671,23 @@ public class SignatureAttribute extends AttributeInfo {
             arguments = targs;
         }
 
+        /**
+         * A class type representing <code>java.lang.Object</code>.
+         */
+        public static ClassType OBJECT = new ClassType("java.lang.Object", null); 
+
+        /**
+         * Constructs a <code>ClassType</code>.  It represents
+         * the name of a non-nested class.
+         *
+         * @param className     a fully qualified class name.
+         * @param args          type arguments or null.
+         */
+        public ClassType(String className, TypeArgument[] args) {
+            name = className;
+            arguments = args;
+        }
+
         /**
          * Returns the class name.
          */
@@ -523,6 +735,24 @@ public class SignatureAttribute extends AttributeInfo {
 
             return sbuf.toString();
         }
+
+        void encode(StringBuffer sb) {
+            sb.append('L');
+            encode2(sb);
+            sb.append(';');
+        }
+
+        void encode2(StringBuffer sb) {
+            ClassType parent = getDeclaringClass();
+            if (parent != null) {
+                parent.encode2(sb);
+                sb.append('$');
+            }
+
+            sb.append(name.replace('.', '/'));
+            if (arguments != null)
+                TypeArgument.encode(sb, arguments);
+        }
     }
 
     /**
@@ -536,6 +766,19 @@ public class SignatureAttribute extends AttributeInfo {
             parent = p;
         }
 
+        /**
+         * Constructs a <code>NestedClassType</code>.
+         *
+         * @param parent        the class surrounding this class type.
+         * @param className     a simple class name.  It does not include
+         *                      a package name or a parent's class name.
+         * @param args          type parameters or null.
+         */
+        public NestedClassType(ClassType parent, String className, TypeArgument[] args) {
+            super(className, args);
+            this.parent = parent;
+        }
+
         /**
          * Returns the class that declares this nested class.
          * This nested class is a member of that declaring class.
@@ -550,6 +793,12 @@ public class SignatureAttribute extends AttributeInfo {
         int dim;
         Type componentType;
 
+        /**
+         * Constructs an <code>ArrayType</code>.
+         *
+         * @param d         dimension.
+         * @param comp      the component type.
+         */
         public ArrayType(int d, Type comp) {
             dim = d;
             componentType = comp;
@@ -577,6 +826,13 @@ public class SignatureAttribute extends AttributeInfo {
 
             return sbuf.toString();
         }
+
+        void encode(StringBuffer sb) {
+            for (int i = 0; i < dim; i++)
+                sb.append('[');
+
+            componentType.encode(sb);
+        }
     }
 
     /**
@@ -589,6 +845,15 @@ public class SignatureAttribute extends AttributeInfo {
             name = sig.substring(begin, end);
         }
 
+        /**
+         * Constructs a <code>TypeVariable</code>.
+         *
+         * @param name      the name of a type variable.
+         */
+        public TypeVariable(String name) {
+            this.name = name;
+        }
+
         /**
          * Returns the variable name.
          */
@@ -602,13 +867,21 @@ public class SignatureAttribute extends AttributeInfo {
         public String toString() {
             return name;
         }
+
+        void encode(StringBuffer sb) {
+            sb.append('T').append(name).append(';');
+        }
     }
 
     /**
      * Parses the given signature string as a class signature.
      *
-     * @param  sig                  the signature.
+     * @param  sig                  the signature obtained from the <code>SignatureAttribute</code>
+     *                              of a <code>ClassFile</code>.
+     * @return  a tree-like data structure representing a class signature.  It provides
+     *          convenient accessor methods.
      * @throws BadBytecode          thrown when a syntactical error is found.
+     * @see #getSignature()
      * @since 3.5
      */
     public static ClassSignature toClassSignature(String sig) throws BadBytecode {
@@ -623,8 +896,12 @@ public class SignatureAttribute extends AttributeInfo {
     /**
      * Parses the given signature string as a method type signature.
      *
-     * @param  sig                  the signature.
+     * @param  sig                  the signature obtained from the <code>SignatureAttribute</code>
+     *                              of a <code>MethodInfo</code>.
+     * @return  @return  a tree-like data structure representing a method signature.  It provides
+     *          convenient accessor methods.
      * @throws BadBytecode          thrown when a syntactical error is found.
+     * @see #getSignature()
      * @since 3.5
      */
     public static MethodSignature toMethodSignature(String sig) throws BadBytecode {
@@ -639,9 +916,11 @@ public class SignatureAttribute extends AttributeInfo {
     /**
      * Parses the given signature string as a field type signature.
      *
-     * @param  sig                  the signature string.
+     * @param  sig                  the signature string obtained from the <code>SignatureAttribute</code>
+     *                              of a <code>FieldInfo</code>.
      * @return the field type signature.
      * @throws BadBytecode          thrown when a syntactical error is found.
+     * @see #getSignature()
      * @since 3.5
      */
     public static ObjectType toFieldSignature(String sig) throws BadBytecode {
index f73190083606be6bce1d4337822a8fe62ad94663..2a732cf413329327aa77952a9011184499a62a5a 100644 (file)
@@ -724,4 +724,42 @@ public class JvstTest4 extends JvstTestRoot {
         Object obj = make(cc.getName());
         assertEquals(1, invoke(obj, "run"));
     }
+
+    public void testGenericSignature() throws Exception {
+        CtClass cc = sloader.makeClass("test4.GenSig");
+        CtClass objClass = sloader.get(CtClass.javaLangObject);
+        SignatureAttribute.ClassSignature cs
+            = new SignatureAttribute.ClassSignature(
+                    new SignatureAttribute.TypeParameter[] {
+                            new SignatureAttribute.TypeParameter("T") });
+        cc.setGenericSignature(cs.encode());    // <T:Ljava/lang/Object;>Ljava/lang/Object;
+
+        CtField f = new CtField(objClass, "value", cc);
+        SignatureAttribute.TypeVariable tvar = new SignatureAttribute.TypeVariable("T");
+        f.setGenericSignature(tvar.encode());   // TT;
+        cc.addField(f);
+
+        CtMethod m = CtNewMethod.make("public Object get(){return value;}", cc);
+        SignatureAttribute.MethodSignature ms
+            = new SignatureAttribute.MethodSignature(null, null, tvar, null);
+        m.setGenericSignature(ms.encode());     // ()TT;
+        cc.addMethod(m);
+
+        CtMethod m2 = CtNewMethod.make("public void set(Object v){value = v;}", cc);
+        SignatureAttribute.MethodSignature ms2
+            = new SignatureAttribute.MethodSignature(null, new SignatureAttribute.Type[] { tvar },
+                                                     new SignatureAttribute.BaseType("void"), null);
+        m2.setGenericSignature(ms2.encode());   // (TT;)V;
+        cc.addMethod(m2);
+
+        cc.writeFile();
+        Object obj = make(cc.getName());
+        Class clazz = obj.getClass();
+        assertEquals("T", clazz.getTypeParameters()[0].getName());
+        assertEquals("T", ((java.lang.reflect.TypeVariable)clazz.getDeclaredField("value").getGenericType()).getName());
+        java.lang.reflect.Method rm = clazz.getDeclaredMethod("get", new Class[0]);
+        assertEquals("T", ((java.lang.reflect.TypeVariable)rm.getGenericReturnType()).getName());
+        java.lang.reflect.Method rm2 = clazz.getDeclaredMethod("set", new Class[] { Object.class });
+        assertEquals("T", ((java.lang.reflect.TypeVariable)rm2.getGenericParameterTypes()[0]).getName());
+    }
 }
index 44bfd9eb3f207c1144fe738f685366be8e133d76..f7bb622a5f6dd6645c073f5bf465b0956c275b5b 100644 (file)
@@ -26,7 +26,8 @@ Shigeru Chiba
 <br>6. <a href="tutorial3.html#generics">Generics</a>
 <br>7. <a href="tutorial3.html#varargs">Varargs</a>
 <br>8. <a href="tutorial3.html#j2me">J2ME</a>
-<br>9. <a href="tutorial3.html#debug">Debug</a>
+<br>9. <a href="tutorial3.html#boxing">Boxing/Unboxing</a>
+<br>10. <a href="tutorial3.html#debug">Debug</a>
 </ul>
 
 <p><br>
index e07372bdaeec1ac14bf251fcd90d4389f1e99951..7c505e0b3d24016b2a340ad4f7e7aa48e1a6b034 100644 (file)
@@ -28,7 +28,9 @@
 
 <p><a href="#j2me">8. J2ME</a>
 
-<p><a href="#debug">9. Debug</a>
+<p><a href="#boxing">9. Boxing/Unboxing
+
+<p><a href="#debug">10. Debug</a>
 
 <p><br>
 
@@ -296,6 +298,11 @@ public Object get() { return value; }
 
 <p>Note that no type parameters are necessary.
 
+<p>However, if you need to make type parameters accessible through reflection
+during runtime, you have to add generic signatures to the class file.
+For more details, see the API documentation (javadoc) of the
+<code>setGenericSignature</code> method in the <code>CtClass</code>.
+
 <p><br>
 
 <h2><a name="varargs">7. Varargs</a></h2>
@@ -359,7 +366,26 @@ objects, call the <code>getDeclaredMethods</code> method on a <code>CtClass</cod
 
 <p><br>
 
-<h2><a name="debug">9. Debug</h2>
+<h2><a name="boxing">9. Boxing/Unboxing</h2>
+
+<p>Boxing and unboxing in Java are syntactic sugar.  There is no bytecode for
+boxing or unboxing.  So the compiler of Javassist does not support them.
+For example, the following statement is valid in Java:
+
+<ul><pre>
+Integer i = 3;
+</pre></ul>
+
+<p>since boxing is implicitly performed.  For Javassist, however, you must explicitly
+convert a value type from <code>int</code> to <code>Integer</code>:
+
+<ul><pre>
+Integer i = new Integer(3);
+</pre></ul>
+
+<p><br>
+
+<h2><a name="debug">10. Debug</h2>
 
 <p>Set <code>CtClass.debugDump</code> to a directory name.
 Then all class files modified and generated by Javassist are saved in that