1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.IOException;
20import java.util.Map;
21import java.util.ArrayList;
22import javassist.CtClass;
23
24/**
25 * <code>Signature_attribute</code>.
26 */
27public class SignatureAttribute extends AttributeInfo {
28    /**
29     * The name of this attribute <code>"Signature"</code>.
30     */
31    public static final String tag = "Signature";
32
33    SignatureAttribute(ConstPool cp, int n, DataInputStream in)
34        throws IOException
35    {
36        super(cp, n, in);
37    }
38
39    /**
40     * Constructs a Signature attribute.
41     *
42     * @param cp                a constant pool table.
43     * @param signature         the signature represented by this attribute.
44     */
45    public SignatureAttribute(ConstPool cp, String signature) {
46        super(cp, tag);
47        int index = cp.addUtf8Info(signature);
48        byte[] bvalue = new byte[2];
49        bvalue[0] = (byte)(index >>> 8);
50        bvalue[1] = (byte)index;
51        set(bvalue);
52    }
53
54    /**
55     * Returns the signature indicated by <code>signature_index</code>.
56     *
57     * @see #toClassSignature(String)
58     * @see #toMethodSignature(String)
59     */
60    public String getSignature() {
61        return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0));
62    }
63
64    /**
65     * Sets <code>signature_index</code> to the index of the given signature,
66     * which is added to a constant pool.
67     *
68     * @param sig       new signature.
69     * @since 3.11
70     */
71    public void setSignature(String sig) {
72        int index = getConstPool().addUtf8Info(sig);
73        ByteArray.write16bit(index, info, 0);
74    }
75
76    /**
77     * Makes a copy.  Class names are replaced according to the
78     * given <code>Map</code> object.
79     *
80     * @param newCp     the constant pool table used by the new copy.
81     * @param classnames        pairs of replaced and substituted
82     *                          class names.
83     */
84    public AttributeInfo copy(ConstPool newCp, Map classnames) {
85        return new SignatureAttribute(newCp, getSignature());
86    }
87
88    void renameClass(String oldname, String newname) {
89        String sig = renameClass(getSignature(), oldname, newname);
90        setSignature(sig);
91    }
92
93    void renameClass(Map classnames) {
94        String sig = renameClass(getSignature(), classnames);
95        setSignature(sig);
96    }
97
98    static String renameClass(String desc, String oldname, String newname) {
99        Map map = new java.util.HashMap();
100        map.put(oldname, newname);
101        return renameClass(desc, map);
102    }
103
104    static String renameClass(String desc, Map map) {
105        if (map == null)
106            return desc;
107
108        StringBuilder newdesc = new StringBuilder();
109        int head = 0;
110        int i = 0;
111        for (;;) {
112            int j = desc.indexOf('L', i);
113            if (j < 0)
114                break;
115
116            StringBuilder nameBuf = new StringBuilder();
117            int k = j;
118            char c;
119            try {
120                while ((c = desc.charAt(++k)) != ';') {
121                    nameBuf.append(c);
122                    if (c == '<') {
123                        while ((c = desc.charAt(++k)) != '>')
124                            nameBuf.append(c);
125
126                        nameBuf.append(c);
127                    }
128                }
129            }
130            catch (IndexOutOfBoundsException e) { break; }
131            i = k + 1;
132            String name = nameBuf.toString();
133            String name2 = (String)map.get(name);
134            if (name2 != null) {
135                newdesc.append(desc.substring(head, j));
136                newdesc.append('L');
137                newdesc.append(name2);
138                newdesc.append(c);
139                head = i;
140            }
141        }
142
143        if (head == 0)
144            return desc;
145        else {
146            int len = desc.length();
147            if (head < len)
148                newdesc.append(desc.substring(head, len));
149
150            return newdesc.toString();
151        }
152    }
153
154    private static boolean isNamePart(int c) {
155        return c != ';' && c != '<';
156    }
157
158    static private class Cursor {
159        int position = 0;
160
161        int indexOf(String s, int ch) throws BadBytecode {
162            int i = s.indexOf(ch, position);
163            if (i < 0)
164                throw error(s);
165            else {
166                position = i + 1;
167                return i;
168            }
169        }
170    }
171
172    /**
173     * Class signature.
174     */
175    public static class ClassSignature {
176        TypeParameter[] params;
177        ClassType superClass;
178        ClassType[] interfaces;
179        ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) {
180            params = p;
181            superClass = s;
182            interfaces = i;
183        }
184
185        /**
186         * Returns the type parameters.
187         *
188         * @return a zero-length array if the type parameters are not specified.
189         */
190        public TypeParameter[] getParameters() {
191            return params;
192        }
193
194        /**
195         * Returns the super class.
196         */
197        public ClassType getSuperClass() { return superClass; }
198
199        /**
200         * Returns the super interfaces.
201         *
202         * @return a zero-length array if the super interfaces are not specified.
203         */
204        public ClassType[] getInterfaces() { return interfaces; }
205
206        /**
207         * Returns the string representation.
208         */
209        public String toString() {
210            StringBuffer sbuf = new StringBuffer();
211
212            TypeParameter.toString(sbuf, params);
213            sbuf.append(" extends ").append(superClass);
214            if (interfaces.length > 0) {
215                sbuf.append(" implements ");
216                Type.toString(sbuf, interfaces);
217            }
218
219            return sbuf.toString();
220        }
221    }
222
223    /**
224     * Method type signature.
225     */
226    public static class MethodSignature {
227        TypeParameter[] typeParams;
228        Type[] params;
229        Type retType;
230        ObjectType[] exceptions;
231
232        MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) {
233            typeParams = tp;
234            params = p;
235            retType = ret;
236            exceptions = ex;
237        }
238
239        /**
240         * Returns the formal type parameters.
241         *
242         * @return a zero-length array if the type parameters are not specified.
243         */
244        public TypeParameter[] getTypeParameters() { return typeParams; }
245
246        /**
247         * Returns the types of the formal parameters.
248         *
249         * @return a zero-length array if no formal parameter is taken.
250         */
251        public Type[] getParameterTypes() { return params; }
252
253        /**
254         * Returns the type of the returned value.
255         */
256        public Type getReturnType() { return retType; }
257
258        /**
259         * Returns the types of the exceptions that may be thrown.
260         *
261         * @return a zero-length array if exceptions are never thrown or
262         * the exception types are not parameterized types or type variables.
263         */
264        public ObjectType[] getExceptionTypes() { return exceptions; }
265
266        /**
267         * Returns the string representation.
268         */
269        public String toString() {
270            StringBuffer sbuf = new StringBuffer();
271
272            TypeParameter.toString(sbuf, typeParams);
273            sbuf.append(" (");
274            Type.toString(sbuf, params);
275            sbuf.append(") ");
276            sbuf.append(retType);
277            if (exceptions.length > 0) {
278                sbuf.append(" throws ");
279                Type.toString(sbuf, exceptions);
280            }
281
282            return sbuf.toString();
283        }
284    }
285
286    /**
287     * Formal type parameters.
288     */
289    public static class TypeParameter {
290        String name;
291        ObjectType superClass;
292        ObjectType[] superInterfaces;
293
294        TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) {
295            name = sig.substring(nb, ne);
296            superClass = sc;
297            superInterfaces = si;
298        }
299
300        /**
301         * Returns the name of the type parameter.
302         */
303        public String getName() {
304            return name;
305        }
306
307        /**
308         * Returns the class bound of this parameter.
309         *
310         * @return null if the class bound is not specified.
311         */
312        public ObjectType getClassBound() { return superClass; }
313
314        /**
315         * Returns the interface bound of this parameter.
316         *
317         * @return a zero-length array if the interface bound is not specified.
318         */
319        public ObjectType[] getInterfaceBound() { return superInterfaces; }
320
321        /**
322         * Returns the string representation.
323         */
324        public String toString() {
325            StringBuffer sbuf = new StringBuffer(getName());
326            if (superClass != null)
327                sbuf.append(" extends ").append(superClass.toString());
328
329            int len = superInterfaces.length;
330            if (len > 0) {
331                for (int i = 0; i < len; i++) {
332                    if (i > 0 || superClass != null)
333                        sbuf.append(" & ");
334                    else
335                        sbuf.append(" extends ");
336
337                    sbuf.append(superInterfaces[i].toString());
338                }
339            }
340
341            return sbuf.toString();
342        }
343
344        static void toString(StringBuffer sbuf, TypeParameter[] tp) {
345            sbuf.append('<');
346            for (int i = 0; i < tp.length; i++) {
347                if (i > 0)
348                    sbuf.append(", ");
349
350                sbuf.append(tp[i]);
351            }
352
353            sbuf.append('>');
354        }
355    }
356
357    /**
358     * Type argument.
359     */
360    public static class TypeArgument {
361        ObjectType arg;
362        char wildcard;
363
364        TypeArgument(ObjectType a, char w) {
365            arg = a;
366            wildcard = w;
367        }
368
369        /**
370         * Returns the kind of this type argument.
371         *
372         * @return <code>' '</code> (not-wildcard), <code>'*'</code> (wildcard), <code>'+'</code> (wildcard with
373         * upper bound), or <code>'-'</code> (wildcard with lower bound).
374         */
375        public char getKind() { return wildcard; }
376
377        /**
378         * Returns true if this type argument is a wildcard type
379         * such as <code>?</code>, <code>? extends String</code>, or <code>? super Integer</code>.
380         */
381        public boolean isWildcard() { return wildcard != ' '; }
382
383        /**
384         * Returns the type represented by this argument
385         * if the argument is not a wildcard type.  Otherwise, this method
386         * returns the upper bound (if the kind is '+'),
387         * the lower bound (if the kind is '-'), or null (if the upper or lower
388         * bound is not specified).
389         */
390        public ObjectType getType() { return arg; }
391
392        /**
393         * Returns the string representation.
394         */
395        public String toString() {
396            if (wildcard == '*')
397                return "?";
398
399            String type = arg.toString();
400            if (wildcard == ' ')
401                return type;
402            else if (wildcard == '+')
403                return "? extends " + type;
404            else
405                return "? super " + type;
406        }
407    }
408
409    /**
410     * Primitive types and object types.
411     */
412    public static abstract class Type {
413        static void toString(StringBuffer sbuf, Type[] ts) {
414            for (int i = 0; i < ts.length; i++) {
415                if (i > 0)
416                    sbuf.append(", ");
417
418                sbuf.append(ts[i]);
419            }
420        }
421    }
422
423    /**
424     * Primitive types.
425     */
426    public static class BaseType extends Type {
427        char descriptor;
428        BaseType(char c) { descriptor = c; }
429
430        /**
431         * Returns the descriptor representing this primitive type.
432         *
433         * @see javassist.bytecode.Descriptor
434         */
435        public char getDescriptor() { return descriptor; }
436
437        /**
438         * Returns the <code>CtClass</code> representing this
439         * primitive type.
440         */
441        public CtClass getCtlass() {
442            return Descriptor.toPrimitiveClass(descriptor);
443        }
444
445        /**
446         * Returns the string representation.
447         */
448        public String toString() {
449            return Descriptor.toClassName(Character.toString(descriptor));
450        }
451    }
452
453    /**
454     * Class types, array types, and type variables.
455     */
456    public static abstract class ObjectType extends Type {}
457
458    /**
459     * Class types.
460     */
461    public static class ClassType extends ObjectType {
462        String name;
463        TypeArgument[] arguments;
464
465        static ClassType make(String s, int b, int e,
466                              TypeArgument[] targs, ClassType parent) {
467            if (parent == null)
468                return new ClassType(s, b, e, targs);
469            else
470                return new NestedClassType(s, b, e, targs, parent);
471        }
472
473        ClassType(String signature, int begin, int end, TypeArgument[] targs) {
474            name = signature.substring(begin, end).replace('/', '.');
475            arguments = targs;
476        }
477
478        /**
479         * Returns the class name.
480         */
481        public String getName() {
482            return name;
483        }
484
485        /**
486         * Returns the type arguments.
487         *
488         * @return null if no type arguments are given to this class.
489         */
490        public TypeArgument[] getTypeArguments() { return arguments; }
491
492        /**
493         * If this class is a member of another class, returns the
494         * class in which this class is declared.
495         *
496         * @return null if this class is not a member of another class.
497         */
498        public ClassType getDeclaringClass() { return null; }
499
500        /**
501         * Returns the string representation.
502         */
503        public String toString() {
504            StringBuffer sbuf = new StringBuffer();
505            ClassType parent = getDeclaringClass();
506            if (parent != null)
507                sbuf.append(parent.toString()).append('.');
508
509            sbuf.append(name);
510            if (arguments != null) {
511                sbuf.append('<');
512                int n = arguments.length;
513                for (int i = 0; i < n; i++) {
514                    if (i > 0)
515                        sbuf.append(", ");
516
517                    sbuf.append(arguments[i].toString());
518                }
519
520                sbuf.append('>');
521            }
522
523            return sbuf.toString();
524        }
525    }
526
527    /**
528     * Nested class types.
529     */
530    public static class NestedClassType extends ClassType {
531        ClassType parent;
532        NestedClassType(String s, int b, int e,
533                        TypeArgument[] targs, ClassType p) {
534            super(s, b, e, targs);
535            parent = p;
536        }
537
538        /**
539         * Returns the class that declares this nested class.
540         * This nested class is a member of that declaring class.
541         */
542        public ClassType getDeclaringClass() { return parent; }
543    }
544
545    /**
546     * Array types.
547     */
548    public static class ArrayType extends ObjectType {
549        int dim;
550        Type componentType;
551
552        public ArrayType(int d, Type comp) {
553            dim = d;
554            componentType = comp;
555        }
556
557        /**
558         * Returns the dimension of the array.
559         */
560        public int getDimension() { return dim; }
561
562        /**
563         * Returns the component type.
564         */
565        public Type getComponentType() {
566            return componentType;
567        }
568
569        /**
570         * Returns the string representation.
571         */
572        public String toString() {
573            StringBuffer sbuf = new StringBuffer(componentType.toString());
574            for (int i = 0; i < dim; i++)
575                sbuf.append("[]");
576
577            return sbuf.toString();
578        }
579    }
580
581    /**
582     * Type variables.
583     */
584    public static class TypeVariable extends ObjectType {
585        String name;
586
587        TypeVariable(String sig, int begin, int end) {
588            name = sig.substring(begin, end);
589        }
590
591        /**
592         * Returns the variable name.
593         */
594        public String getName() {
595            return name;
596        }
597
598        /**
599         * Returns the string representation.
600         */
601        public String toString() {
602            return name;
603        }
604    }
605
606    /**
607     * Parses the given signature string as a class signature.
608     *
609     * @param  sig                  the signature.
610     * @throws BadBytecode          thrown when a syntactical error is found.
611     * @since 3.5
612     */
613    public static ClassSignature toClassSignature(String sig) throws BadBytecode {
614        try {
615            return parseSig(sig);
616        }
617        catch (IndexOutOfBoundsException e) {
618            throw error(sig);
619        }
620    }
621
622    /**
623     * Parses the given signature string as a method type signature.
624     *
625     * @param  sig                  the signature.
626     * @throws BadBytecode          thrown when a syntactical error is found.
627     * @since 3.5
628     */
629    public static MethodSignature toMethodSignature(String sig) throws BadBytecode {
630        try {
631            return parseMethodSig(sig);
632        }
633        catch (IndexOutOfBoundsException e) {
634            throw error(sig);
635        }
636    }
637
638    /**
639     * Parses the given signature string as a field type signature.
640     *
641     * @param  sig                  the signature string.
642     * @return the field type signature.
643     * @throws BadBytecode          thrown when a syntactical error is found.
644     * @since 3.5
645     */
646    public static ObjectType toFieldSignature(String sig) throws BadBytecode {
647        try {
648            return parseObjectType(sig, new Cursor(), false);
649        }
650        catch (IndexOutOfBoundsException e) {
651            throw error(sig);
652        }
653    }
654
655    private static ClassSignature parseSig(String sig)
656        throws BadBytecode, IndexOutOfBoundsException
657    {
658        Cursor cur = new Cursor();
659        TypeParameter[] tp = parseTypeParams(sig, cur);
660        ClassType superClass = parseClassType(sig, cur);
661        int sigLen = sig.length();
662        ArrayList ifArray = new ArrayList();
663        while (cur.position < sigLen && sig.charAt(cur.position) == 'L')
664            ifArray.add(parseClassType(sig, cur));
665
666        ClassType[] ifs
667            = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]);
668        return new ClassSignature(tp, superClass, ifs);
669    }
670
671    private static MethodSignature parseMethodSig(String sig)
672        throws BadBytecode
673    {
674        Cursor cur = new Cursor();
675        TypeParameter[] tp = parseTypeParams(sig, cur);
676        if (sig.charAt(cur.position++) != '(')
677            throw error(sig);
678
679        ArrayList params = new ArrayList();
680        while (sig.charAt(cur.position) != ')') {
681            Type t = parseType(sig, cur);
682            params.add(t);
683        }
684
685        cur.position++;
686        Type ret = parseType(sig, cur);
687        int sigLen = sig.length();
688        ArrayList exceptions = new ArrayList();
689        while (cur.position < sigLen && sig.charAt(cur.position) == '^') {
690            cur.position++;
691            ObjectType t = parseObjectType(sig, cur, false);
692            if (t instanceof ArrayType)
693                throw error(sig);
694
695            exceptions.add(t);
696        }
697
698        Type[] p = (Type[])params.toArray(new Type[params.size()]);
699        ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]);
700        return new MethodSignature(tp, p, ret, ex);
701    }
702
703    private static TypeParameter[] parseTypeParams(String sig, Cursor cur)
704        throws BadBytecode
705    {
706        ArrayList typeParam = new ArrayList();
707        if (sig.charAt(cur.position) == '<') {
708            cur.position++;
709            while (sig.charAt(cur.position) != '>') {
710                int nameBegin = cur.position;
711                int nameEnd = cur.indexOf(sig, ':');
712                ObjectType classBound = parseObjectType(sig, cur, true);
713                ArrayList ifBound = new ArrayList();
714                while (sig.charAt(cur.position) == ':') {
715                    cur.position++;
716                    ObjectType t = parseObjectType(sig, cur, false);
717                    ifBound.add(t);
718                }
719
720                TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd,
721                        classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()]));
722                typeParam.add(p);
723            }
724
725            cur.position++;
726        }
727
728        return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]);
729    }
730
731    private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow)
732        throws BadBytecode
733    {
734        int i;
735        int begin = c.position;
736        switch (sig.charAt(begin)) {
737        case 'L' :
738            return parseClassType2(sig, c, null);
739        case 'T' :
740            i = c.indexOf(sig, ';');
741            return new TypeVariable(sig, begin + 1, i);
742        case '[' :
743            return parseArray(sig, c);
744        default :
745            if (dontThrow)
746                return null;
747            else
748                throw error(sig);
749        }
750    }
751
752    private static ClassType parseClassType(String sig, Cursor c)
753        throws BadBytecode
754    {
755        if (sig.charAt(c.position) == 'L')
756            return parseClassType2(sig, c, null);
757        else
758            throw error(sig);
759    }
760
761    private static ClassType parseClassType2(String sig, Cursor c, ClassType parent)
762        throws BadBytecode
763    {
764        int start = ++c.position;
765        char t;
766        do {
767            t = sig.charAt(c.position++);
768        } while (t != '$' && t != '<' && t != ';');
769        int end = c.position - 1;
770        TypeArgument[] targs;
771        if (t == '<') {
772            targs = parseTypeArgs(sig, c);
773            t = sig.charAt(c.position++);
774        }
775        else
776            targs = null;
777
778        ClassType thisClass = ClassType.make(sig, start, end, targs, parent);
779        if (t == '$') {
780            c.position--;
781            return parseClassType2(sig, c, thisClass);
782        }
783        else
784            return thisClass;
785    }
786
787    private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode {
788        ArrayList args = new ArrayList();
789        char t;
790        while ((t = sig.charAt(c.position++)) != '>') {
791            TypeArgument ta;
792            if (t == '*' )
793                ta = new TypeArgument(null, '*');
794            else {
795                if (t != '+' && t != '-') {
796                    t = ' ';
797                    c.position--;
798                }
799
800                ta = new TypeArgument(parseObjectType(sig, c, false), t);
801            }
802
803            args.add(ta);
804        }
805
806        return (TypeArgument[])args.toArray(new TypeArgument[args.size()]);
807    }
808
809    private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode {
810        int dim = 1;
811        while (sig.charAt(++c.position) == '[')
812            dim++;
813
814        return new ArrayType(dim, parseType(sig, c));
815    }
816
817    private static Type parseType(String sig, Cursor c) throws BadBytecode {
818        Type t = parseObjectType(sig, c, true);
819        if (t == null)
820            t = new BaseType(sig.charAt(c.position++));
821
822        return t;
823    }
824
825    private static BadBytecode error(String sig) {
826        return new BadBytecode("bad signature: " + sig);
827    }
828}
829