• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 package javassist.bytecode;
17 
18 import java.io.DataInputStream;
19 import java.io.IOException;
20 import java.util.Map;
21 import java.util.ArrayList;
22 import javassist.CtClass;
23 
24 /**
25  * <code>Signature_attribute</code>.
26  */
27 public class SignatureAttribute extends AttributeInfo {
28     /**
29      * The name of this attribute <code>"Signature"</code>.
30      */
31     public static final String tag = "Signature";
32 
SignatureAttribute(ConstPool cp, int n, DataInputStream in)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      */
SignatureAttribute(ConstPool cp, String signature)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      */
getSignature()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      */
setSignature(String sig)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      */
copy(ConstPool newCp, Map classnames)84     public AttributeInfo copy(ConstPool newCp, Map classnames) {
85         return new SignatureAttribute(newCp, getSignature());
86     }
87 
renameClass(String oldname, String newname)88     void renameClass(String oldname, String newname) {
89         String sig = renameClass(getSignature(), oldname, newname);
90         setSignature(sig);
91     }
92 
renameClass(Map classnames)93     void renameClass(Map classnames) {
94         String sig = renameClass(getSignature(), classnames);
95         setSignature(sig);
96     }
97 
renameClass(String desc, String oldname, String newname)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 
renameClass(String desc, Map map)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 
isNamePart(int c)154     private static boolean isNamePart(int c) {
155         return c != ';' && c != '<';
156     }
157 
158     static private class Cursor {
159         int position = 0;
160 
indexOf(String s, int ch)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;
ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i)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          */
getParameters()190         public TypeParameter[] getParameters() {
191             return params;
192         }
193 
194         /**
195          * Returns the super class.
196          */
getSuperClass()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          */
getInterfaces()204         public ClassType[] getInterfaces() { return interfaces; }
205 
206         /**
207          * Returns the string representation.
208          */
toString()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 
MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex)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          */
getTypeParameters()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          */
getParameterTypes()251         public Type[] getParameterTypes() { return params; }
252 
253         /**
254          * Returns the type of the returned value.
255          */
getReturnType()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          */
getExceptionTypes()264         public ObjectType[] getExceptionTypes() { return exceptions; }
265 
266         /**
267          * Returns the string representation.
268          */
toString()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 
TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si)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          */
getName()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          */
getClassBound()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          */
getInterfaceBound()319         public ObjectType[] getInterfaceBound() { return superInterfaces; }
320 
321         /**
322          * Returns the string representation.
323          */
toString()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 
toString(StringBuffer sbuf, TypeParameter[] tp)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 
TypeArgument(ObjectType a, char w)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          */
getKind()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          */
isWildcard()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          */
getType()390         public ObjectType getType() { return arg; }
391 
392         /**
393          * Returns the string representation.
394          */
toString()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 {
toString(StringBuffer sbuf, Type[] ts)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;
BaseType(char c)428         BaseType(char c) { descriptor = c; }
429 
430         /**
431          * Returns the descriptor representing this primitive type.
432          *
433          * @see javassist.bytecode.Descriptor
434          */
getDescriptor()435         public char getDescriptor() { return descriptor; }
436 
437         /**
438          * Returns the <code>CtClass</code> representing this
439          * primitive type.
440          */
getCtlass()441         public CtClass getCtlass() {
442             return Descriptor.toPrimitiveClass(descriptor);
443         }
444 
445         /**
446          * Returns the string representation.
447          */
toString()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 
make(String s, int b, int e, TypeArgument[] targs, ClassType parent)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 
ClassType(String signature, int begin, int end, TypeArgument[] targs)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          */
getName()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          */
getTypeArguments()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          */
getDeclaringClass()498         public ClassType getDeclaringClass() { return null; }
499 
500         /**
501          * Returns the string representation.
502          */
toString()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;
NestedClassType(String s, int b, int e, TypeArgument[] targs, ClassType p)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          */
getDeclaringClass()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 
ArrayType(int d, Type comp)552         public ArrayType(int d, Type comp) {
553             dim = d;
554             componentType = comp;
555         }
556 
557         /**
558          * Returns the dimension of the array.
559          */
getDimension()560         public int getDimension() { return dim; }
561 
562         /**
563          * Returns the component type.
564          */
getComponentType()565         public Type getComponentType() {
566             return componentType;
567         }
568 
569         /**
570          * Returns the string representation.
571          */
toString()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 
TypeVariable(String sig, int begin, int end)587         TypeVariable(String sig, int begin, int end) {
588             name = sig.substring(begin, end);
589         }
590 
591         /**
592          * Returns the variable name.
593          */
getName()594         public String getName() {
595             return name;
596         }
597 
598         /**
599          * Returns the string representation.
600          */
toString()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      */
toClassSignature(String sig)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      */
toMethodSignature(String sig)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      */
toFieldSignature(String sig)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 
parseSig(String sig)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 
parseMethodSig(String sig)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 
parseTypeParams(String sig, Cursor cur)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 
parseObjectType(String sig, Cursor c, boolean dontThrow)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 
parseClassType(String sig, Cursor c)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 
parseClassType2(String sig, Cursor c, ClassType parent)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 
parseTypeArgs(String sig, Cursor c)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 
parseArray(String sig, Cursor c)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 
parseType(String sig, Cursor c)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 
error(String sig)825     private static BadBytecode error(String sig) {
826         return new BadBytecode("bad signature: " + sig);
827     }
828 }
829