1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.classfile;
18
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.CharArrayReader;
22 import java.io.CharArrayWriter;
23 import java.io.FilterReader;
24 import java.io.FilterWriter;
25 import java.io.IOException;
26 import java.io.PrintStream;
27 import java.io.PrintWriter;
28 import java.io.Reader;
29 import java.io.Writer;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Locale;
33 import java.util.zip.GZIPInputStream;
34 import java.util.zip.GZIPOutputStream;
35 import org.apache.bcel.Constants;
36 import org.apache.bcel.util.ByteSequence;
37
38 /***
39 * Utility functions that do not really belong to any class in particular.
40 *
41 * @version $Id: Utility.java 386056 2006-03-15 11:31:56Z tcurdt $
42 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
43 */
44 public abstract class Utility {
45
46 private static int unwrap( ThreadLocal tl ) {
47 return ((Integer) tl.get()).intValue();
48 }
49
50
51 private static void wrap( ThreadLocal tl, int value ) {
52 tl.set(new Integer(value));
53 }
54
55 private static ThreadLocal consumed_chars = new ThreadLocal() {
56
57 protected Object initialValue() {
58 return new Integer(0);
59 }
60 };
61
62
63
64
65 private static boolean wide = false;
66
67
68
69
70
71
72
73
74
75
76 /***
77 * Convert bit field of flags into string such as `static final'.
78 *
79 * @param access_flags Access flags
80 * @return String representation of flags
81 */
82 public static final String accessToString( int access_flags ) {
83 return accessToString(access_flags, false);
84 }
85
86
87 /***
88 * Convert bit field of flags into string such as `static final'.
89 *
90 * Special case: Classes compiled with new compilers and with the
91 * `ACC_SUPER' flag would be said to be "synchronized". This is
92 * because SUN used the same value for the flags `ACC_SUPER' and
93 * `ACC_SYNCHRONIZED'.
94 *
95 * @param access_flags Access flags
96 * @param for_class access flags are for class qualifiers ?
97 * @return String representation of flags
98 */
99 public static final String accessToString( int access_flags, boolean for_class ) {
100 StringBuffer buf = new StringBuffer();
101 int p = 0;
102 for (int i = 0; p < Constants.MAX_ACC_FLAG; i++) {
103 p = pow2(i);
104 if ((access_flags & p) != 0) {
105
106
107
108
109
110 if (for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE))) {
111 continue;
112 }
113 buf.append(Constants.ACCESS_NAMES[i]).append(" ");
114 }
115 }
116 return buf.toString().trim();
117 }
118
119
120 /***
121 * @return "class" or "interface", depending on the ACC_INTERFACE flag
122 */
123 public static final String classOrInterface( int access_flags ) {
124 return ((access_flags & Constants.ACC_INTERFACE) != 0) ? "interface" : "class";
125 }
126
127
128 /***
129 * Disassemble a byte array of JVM byte codes starting from code line
130 * `index' and return the disassembled string representation. Decode only
131 * `num' opcodes (including their operands), use -1 if you want to
132 * decompile everything.
133 *
134 * @param code byte code array
135 * @param constant_pool Array of constants
136 * @param index offset in `code' array
137 * <EM>(number of opcodes, not bytes!)</EM>
138 * @param length number of opcodes to decompile, -1 for all
139 * @param verbose be verbose, e.g. print constant pool index
140 * @return String representation of byte codes
141 */
142 public static final String codeToString( byte[] code, ConstantPool constant_pool, int index,
143 int length, boolean verbose ) {
144 StringBuffer buf = new StringBuffer(code.length * 20);
145 ByteSequence stream = new ByteSequence(code);
146 try {
147 for (int i = 0; i < index; i++) {
148 codeToString(stream, constant_pool, verbose);
149 }
150 for (int i = 0; stream.available() > 0; i++) {
151 if ((length < 0) || (i < length)) {
152 String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
153 buf.append(indices).append(codeToString(stream, constant_pool, verbose))
154 .append('\n');
155 }
156 }
157 } catch (IOException e) {
158 System.out.println(buf.toString());
159 e.printStackTrace();
160 throw new ClassFormatException("Byte code error: " + e);
161 }
162 return buf.toString();
163 }
164
165
166 public static final String codeToString( byte[] code, ConstantPool constant_pool, int index,
167 int length ) {
168 return codeToString(code, constant_pool, index, length, true);
169 }
170
171
172 /***
173 * Disassemble a stream of byte codes and return the
174 * string representation.
175 *
176 * @param bytes stream of bytes
177 * @param constant_pool Array of constants
178 * @param verbose be verbose, e.g. print constant pool index
179 * @return String representation of byte code
180 */
181 public static final String codeToString( ByteSequence bytes, ConstantPool constant_pool,
182 boolean verbose ) throws IOException {
183 short opcode = (short) bytes.readUnsignedByte();
184 int default_offset = 0, low, high, npairs;
185 int index, vindex, constant;
186 int[] match, jump_table;
187 int no_pad_bytes = 0, offset;
188 StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
189
190
191
192 if ((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
193 int remainder = bytes.getIndex() % 4;
194 no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
195 for (int i = 0; i < no_pad_bytes; i++) {
196 byte b;
197 if ((b = bytes.readByte()) != 0) {
198 System.err.println("Warning: Padding byte != 0 in "
199 + Constants.OPCODE_NAMES[opcode] + ":" + b);
200 }
201 }
202
203 default_offset = bytes.readInt();
204 }
205 switch (opcode) {
206
207
208 case Constants.TABLESWITCH:
209 low = bytes.readInt();
210 high = bytes.readInt();
211 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
212 default_offset += offset;
213 buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low)
214 .append(", high = ").append(high).append("(");
215 jump_table = new int[high - low + 1];
216 for (int i = 0; i < jump_table.length; i++) {
217 jump_table[i] = offset + bytes.readInt();
218 buf.append(jump_table[i]);
219 if (i < jump_table.length - 1) {
220 buf.append(", ");
221 }
222 }
223 buf.append(")");
224 break;
225
226
227 case Constants.LOOKUPSWITCH: {
228 npairs = bytes.readInt();
229 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
230 match = new int[npairs];
231 jump_table = new int[npairs];
232 default_offset += offset;
233 buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append(
234 npairs).append(" (");
235 for (int i = 0; i < npairs; i++) {
236 match[i] = bytes.readInt();
237 jump_table[i] = offset + bytes.readInt();
238 buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")");
239 if (i < npairs - 1) {
240 buf.append(", ");
241 }
242 }
243 buf.append(")");
244 }
245 break;
246
247
248
249 case Constants.GOTO:
250 case Constants.IFEQ:
251 case Constants.IFGE:
252 case Constants.IFGT:
253 case Constants.IFLE:
254 case Constants.IFLT:
255 case Constants.JSR:
256 case Constants.IFNE:
257 case Constants.IFNONNULL:
258 case Constants.IFNULL:
259 case Constants.IF_ACMPEQ:
260 case Constants.IF_ACMPNE:
261 case Constants.IF_ICMPEQ:
262 case Constants.IF_ICMPGE:
263 case Constants.IF_ICMPGT:
264 case Constants.IF_ICMPLE:
265 case Constants.IF_ICMPLT:
266 case Constants.IF_ICMPNE:
267 buf.append("\t\t#").append((bytes.getIndex() - 1) + bytes.readShort());
268 break;
269
270
271 case Constants.GOTO_W:
272 case Constants.JSR_W:
273 buf.append("\t\t#").append(((bytes.getIndex() - 1) + bytes.readInt()));
274 break;
275
276
277 case Constants.ALOAD:
278 case Constants.ASTORE:
279 case Constants.DLOAD:
280 case Constants.DSTORE:
281 case Constants.FLOAD:
282 case Constants.FSTORE:
283 case Constants.ILOAD:
284 case Constants.ISTORE:
285 case Constants.LLOAD:
286 case Constants.LSTORE:
287 case Constants.RET:
288 if (wide) {
289 vindex = bytes.readUnsignedShort();
290 wide = false;
291 } else {
292 vindex = bytes.readUnsignedByte();
293 }
294 buf.append("\t\t%").append(vindex);
295 break;
296
297
298
299
300
301 case Constants.WIDE:
302 wide = true;
303 buf.append("\t(wide)");
304 break;
305
306
307 case Constants.NEWARRAY:
308 buf.append("\t\t<").append(Constants.TYPE_NAMES[bytes.readByte()]).append(">");
309 break;
310
311
312 case Constants.GETFIELD:
313 case Constants.GETSTATIC:
314 case Constants.PUTFIELD:
315 case Constants.PUTSTATIC:
316 index = bytes.readUnsignedShort();
317 buf.append("\t\t").append(
318 constant_pool.constantToString(index, Constants.CONSTANT_Fieldref)).append(
319 (verbose ? " (" + index + ")" : ""));
320 break;
321
322
323 case Constants.NEW:
324 case Constants.CHECKCAST:
325 buf.append("\t");
326 case Constants.INSTANCEOF:
327 index = bytes.readUnsignedShort();
328 buf.append("\t<").append(
329 constant_pool.constantToString(index, Constants.CONSTANT_Class))
330 .append(">").append((verbose ? " (" + index + ")" : ""));
331 break;
332
333
334 case Constants.INVOKESPECIAL:
335 case Constants.INVOKESTATIC:
336 case Constants.INVOKEVIRTUAL:
337 index = bytes.readUnsignedShort();
338 buf.append("\t").append(
339 constant_pool.constantToString(index, Constants.CONSTANT_Methodref))
340 .append((verbose ? " (" + index + ")" : ""));
341 break;
342 case Constants.INVOKEINTERFACE:
343 index = bytes.readUnsignedShort();
344 int nargs = bytes.readUnsignedByte();
345 buf.append("\t").append(
346 constant_pool
347 .constantToString(index, Constants.CONSTANT_InterfaceMethodref))
348 .append(verbose ? " (" + index + ")\t" : "").append(nargs).append("\t")
349 .append(bytes.readUnsignedByte());
350 break;
351
352
353 case Constants.LDC_W:
354 case Constants.LDC2_W:
355 index = bytes.readUnsignedShort();
356 buf.append("\t\t").append(
357 constant_pool.constantToString(index, constant_pool.getConstant(index)
358 .getTag())).append((verbose ? " (" + index + ")" : ""));
359 break;
360 case Constants.LDC:
361 index = bytes.readUnsignedByte();
362 buf.append("\t\t").append(
363 constant_pool.constantToString(index, constant_pool.getConstant(index)
364 .getTag())).append((verbose ? " (" + index + ")" : ""));
365 break;
366
367
368 case Constants.ANEWARRAY:
369 index = bytes.readUnsignedShort();
370 buf.append("\t\t<").append(
371 compactClassName(constant_pool.getConstantString(index,
372 Constants.CONSTANT_Class), false)).append(">").append(
373 (verbose ? " (" + index + ")" : ""));
374 break;
375
376
377 case Constants.MULTIANEWARRAY: {
378 index = bytes.readUnsignedShort();
379 int dimensions = bytes.readUnsignedByte();
380 buf.append("\t<").append(
381 compactClassName(constant_pool.getConstantString(index,
382 Constants.CONSTANT_Class), false)).append(">\t").append(dimensions)
383 .append((verbose ? " (" + index + ")" : ""));
384 }
385 break;
386
387
388 case Constants.IINC:
389 if (wide) {
390 vindex = bytes.readUnsignedShort();
391 constant = bytes.readShort();
392 wide = false;
393 } else {
394 vindex = bytes.readUnsignedByte();
395 constant = bytes.readByte();
396 }
397 buf.append("\t\t%").append(vindex).append("\t").append(constant);
398 break;
399 default:
400 if (Constants.NO_OF_OPERANDS[opcode] > 0) {
401 for (int i = 0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
402 buf.append("\t\t");
403 switch (Constants.TYPE_OF_OPERANDS[opcode][i]) {
404 case Constants.T_BYTE:
405 buf.append(bytes.readByte());
406 break;
407 case Constants.T_SHORT:
408 buf.append(bytes.readShort());
409 break;
410 case Constants.T_INT:
411 buf.append(bytes.readInt());
412 break;
413 default:
414 System.err.println("Unreachable default case reached!");
415 System.exit(-1);
416 }
417 }
418 }
419 }
420 return buf.toString();
421 }
422
423
424 public static final String codeToString( ByteSequence bytes, ConstantPool constant_pool )
425 throws IOException {
426 return codeToString(bytes, constant_pool, true);
427 }
428
429
430 /***
431 * Shorten long class names, <em>java/lang/String</em> becomes
432 * <em>String</em>.
433 *
434 * @param str The long class name
435 * @return Compacted class name
436 */
437 public static final String compactClassName( String str ) {
438 return compactClassName(str, true);
439 }
440
441
442 /***
443 * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
444 * if the
445 * class name starts with this string and the flag <em>chopit</em> is true.
446 * Slashes <em>/</em> are converted to dots <em>.</em>.
447 *
448 * @param str The long class name
449 * @param prefix The prefix the get rid off
450 * @param chopit Flag that determines whether chopping is executed or not
451 * @return Compacted class name
452 */
453 public static final String compactClassName( String str, String prefix, boolean chopit ) {
454 int len = prefix.length();
455 str = str.replace('/', '.');
456 if (chopit) {
457
458 if (str.startsWith(prefix) && (str.substring(len).indexOf('.') == -1)) {
459 str = str.substring(len);
460 }
461 }
462 return str;
463 }
464
465
466 /***
467 * Shorten long class names, <em>java/lang/String</em> becomes
468 * <em>java.lang.String</em>,
469 * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
470 * is also removed.
471 *
472 * @param str The long class name
473 * @param chopit Flag that determines whether chopping is executed or not
474 * @return Compacted class name
475 */
476 public static final String compactClassName( String str, boolean chopit ) {
477 return compactClassName(str, "java.lang.", chopit);
478 }
479
480
481 /***
482 * @return `flag' with bit `i' set to 1
483 */
484 public static final int setBit( int flag, int i ) {
485 return flag | pow2(i);
486 }
487
488
489 /***
490 * @return `flag' with bit `i' set to 0
491 */
492 public static final int clearBit( int flag, int i ) {
493 int bit = pow2(i);
494 return (flag & bit) == 0 ? flag : flag ^ bit;
495 }
496
497
498 /***
499 * @return true, if bit `i' in `flag' is set
500 */
501 public static final boolean isSet( int flag, int i ) {
502 return (flag & pow2(i)) != 0;
503 }
504
505
506 /***
507 * Converts string containing the method return and argument types
508 * to a byte code method signature.
509 *
510 * @param ret Return type of method
511 * @param argv Types of method arguments
512 * @return Byte code representation of method signature
513 */
514 public final static String methodTypeToSignature( String ret, String[] argv )
515 throws ClassFormatException {
516 StringBuffer buf = new StringBuffer("(");
517 String str;
518 if (argv != null) {
519 for (int i = 0; i < argv.length; i++) {
520 str = getSignature(argv[i]);
521 if (str.endsWith("V")) {
522 throw new ClassFormatException("Invalid type: " + argv[i]);
523 }
524 buf.append(str);
525 }
526 }
527 str = getSignature(ret);
528 buf.append(")").append(str);
529 return buf.toString();
530 }
531
532
533 /***
534 * @param signature Method signature
535 * @return Array of argument types
536 * @throws ClassFormatException
537 */
538 public static final String[] methodSignatureArgumentTypes( String signature )
539 throws ClassFormatException {
540 return methodSignatureArgumentTypes(signature, true);
541 }
542
543
544 /***
545 * @param signature Method signature
546 * @param chopit Shorten class names ?
547 * @return Array of argument types
548 * @throws ClassFormatException
549 */
550 public static final String[] methodSignatureArgumentTypes( String signature, boolean chopit )
551 throws ClassFormatException {
552 List vec = new ArrayList();
553 int index;
554 try {
555 if (signature.charAt(0) != '(') {
556 throw new ClassFormatException("Invalid method signature: " + signature);
557 }
558 index = 1;
559 while (signature.charAt(index) != ')') {
560 vec.add(signatureToString(signature.substring(index), chopit));
561
562 index += unwrap(consumed_chars);
563 }
564 } catch (StringIndexOutOfBoundsException e) {
565 throw new ClassFormatException("Invalid method signature: " + signature);
566 }
567 return (String[]) vec.toArray(new String[vec.size()]);
568 }
569
570
571 /***
572 * @param signature Method signature
573 * @return return type of method
574 * @throws ClassFormatException
575 */
576 public static final String methodSignatureReturnType( String signature )
577 throws ClassFormatException {
578 return methodSignatureReturnType(signature, true);
579 }
580
581
582 /***
583 * @param signature Method signature
584 * @param chopit Shorten class names ?
585 * @return return type of method
586 * @throws ClassFormatException
587 */
588 public static final String methodSignatureReturnType( String signature, boolean chopit )
589 throws ClassFormatException {
590 int index;
591 String type;
592 try {
593
594 index = signature.lastIndexOf(')') + 1;
595 type = signatureToString(signature.substring(index), chopit);
596 } catch (StringIndexOutOfBoundsException e) {
597 throw new ClassFormatException("Invalid method signature: " + signature);
598 }
599 return type;
600 }
601
602
603 /***
604 * Converts method signature to string with all class names compacted.
605 *
606 * @param signature to convert
607 * @param name of method
608 * @param access flags of method
609 * @return Human readable signature
610 */
611 public static final String methodSignatureToString( String signature, String name, String access ) {
612 return methodSignatureToString(signature, name, access, true);
613 }
614
615
616 public static final String methodSignatureToString( String signature, String name,
617 String access, boolean chopit ) {
618 return methodSignatureToString(signature, name, access, chopit, null);
619 }
620
621
622 /***
623 * A return?type signature represents the return value from a method.
624 * It is a series of bytes in the following grammar:
625 *
626 * <return_signature> ::= <field_type> | V
627 *
628 * The character V indicates that the method returns no value. Otherwise, the
629 * signature indicates the type of the return value.
630 * An argument signature represents an argument passed to a method:
631 *
632 * <argument_signature> ::= <field_type>
633 *
634 * A method signature represents the arguments that the method expects, and
635 * the value that it returns.
636 * <method_signature> ::= (<arguments_signature>) <return_signature>
637 * <arguments_signature>::= <argument_signature>*
638 *
639 * This method converts such a string into a Java type declaration like
640 * `void main(String[])' and throws a `ClassFormatException' when the parsed
641 * type is invalid.
642 *
643 * @param signature Method signature
644 * @param name Method name
645 * @param access Method access rights
646 * @return Java type declaration
647 * @throws ClassFormatException
648 */
649 public static final String methodSignatureToString( String signature, String name,
650 String access, boolean chopit, LocalVariableTable vars ) throws ClassFormatException {
651 StringBuffer buf = new StringBuffer("(");
652 String type;
653 int index;
654 int var_index = (access.indexOf("static") >= 0) ? 0 : 1;
655 try {
656 if (signature.charAt(0) != '(') {
657 throw new ClassFormatException("Invalid method signature: " + signature);
658 }
659 index = 1;
660 while (signature.charAt(index) != ')') {
661 String param_type = signatureToString(signature.substring(index), chopit);
662 buf.append(param_type);
663 if (vars != null) {
664 LocalVariable l = vars.getLocalVariable(var_index);
665 if (l != null) {
666 buf.append(" ").append(l.getName());
667 }
668 } else {
669 buf.append(" arg").append(var_index);
670 }
671 if ("double".equals(param_type) || "long".equals(param_type)) {
672 var_index += 2;
673 } else {
674 var_index++;
675 }
676 buf.append(", ");
677
678 index += unwrap(consumed_chars);
679 }
680 index++;
681
682 type = signatureToString(signature.substring(index), chopit);
683 } catch (StringIndexOutOfBoundsException e) {
684 throw new ClassFormatException("Invalid method signature: " + signature);
685 }
686 if (buf.length() > 1) {
687 buf.setLength(buf.length() - 2);
688 }
689 buf.append(")");
690 return access + ((access.length() > 0) ? " " : "") +
691 type + " " + name + buf.toString();
692 }
693
694
695
696 private static final int pow2( int n ) {
697 return 1 << n;
698 }
699
700
701 /***
702 * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
703 *
704 * @param str String to permute
705 * @param old String to be replaced
706 * @param new_ Replacement string
707 * @return new String object
708 */
709 public static final String replace( String str, String old, String new_ ) {
710 int index, old_index;
711 StringBuffer buf = new StringBuffer();
712 try {
713 if ((index = str.indexOf(old)) != -1) {
714 old_index = 0;
715
716 while ((index = str.indexOf(old, old_index)) != -1) {
717 buf.append(str.substring(old_index, index));
718 buf.append(new_);
719 old_index = index + old.length();
720 }
721 buf.append(str.substring(old_index));
722 str = buf.toString();
723 }
724 } catch (StringIndexOutOfBoundsException e) {
725 System.err.println(e);
726 }
727 return str;
728 }
729
730
731 /***
732 * Converts signature to string with all class names compacted.
733 *
734 * @param signature to convert
735 * @return Human readable signature
736 */
737 public static final String signatureToString( String signature ) {
738 return signatureToString(signature, true);
739 }
740
741
742 /***
743 * The field signature represents the value of an argument to a function or
744 * the value of a variable. It is a series of bytes generated by the
745 * following grammar:
746 *
747 * <PRE>
748 * <field_signature> ::= <field_type>
749 * <field_type> ::= <base_type>|<object_type>|<array_type>
750 * <base_type> ::= B|C|D|F|I|J|S|Z
751 * <object_type> ::= L<fullclassname>;
752 * <array_type> ::= [<field_type>
753 *
754 * The meaning of the base types is as follows:
755 * B byte signed byte
756 * C char character
757 * D double double precision IEEE float
758 * F float single precision IEEE float
759 * I int integer
760 * J long long integer
761 * L<fullclassname>; ... an object of the given class
762 * S short signed short
763 * Z boolean true or false
764 * [<field sig> ... array
765 * </PRE>
766 *
767 * This method converts this string into a Java type declaration such as
768 * `String[]' and throws a `ClassFormatException' when the parsed type is
769 * invalid.
770 *
771 * @param signature Class signature
772 * @param chopit Flag that determines whether chopping is executed or not
773 * @return Java type declaration
774 * @throws ClassFormatException
775 */
776 public static final String signatureToString( String signature, boolean chopit ) {
777
778 wrap(consumed_chars, 1);
779 try {
780 switch (signature.charAt(0)) {
781 case 'B':
782 return "byte";
783 case 'C':
784 return "char";
785 case 'D':
786 return "double";
787 case 'F':
788 return "float";
789 case 'I':
790 return "int";
791 case 'J':
792 return "long";
793 case 'L': {
794 int index = signature.indexOf(';');
795 if (index < 0) {
796 throw new ClassFormatException("Invalid signature: " + signature);
797 }
798
799 wrap(consumed_chars, index + 1);
800 return compactClassName(signature.substring(1, index), chopit);
801 }
802 case 'S':
803 return "short";
804 case 'Z':
805 return "boolean";
806 case '[': {
807 int n;
808 StringBuffer brackets;
809 String type;
810 int consumed_chars;
811 brackets = new StringBuffer();
812
813 for (n = 0; signature.charAt(n) == '['; n++) {
814 brackets.append("[]");
815 }
816 consumed_chars = n;
817
818 type = signatureToString(signature.substring(n), chopit);
819
820
821 int _temp = unwrap(Utility.consumed_chars) + consumed_chars;
822 wrap(Utility.consumed_chars, _temp);
823 return type + brackets.toString();
824 }
825 case 'V':
826 return "void";
827 default:
828 throw new ClassFormatException("Invalid signature: `" + signature + "'");
829 }
830 } catch (StringIndexOutOfBoundsException e) {
831 throw new ClassFormatException("Invalid signature: " + e + ":" + signature);
832 }
833 }
834
835
836 /*** Parse Java type such as "char", or "java.lang.String[]" and return the
837 * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
838 *
839 * @param type Java type
840 * @return byte code signature
841 */
842 public static String getSignature( String type ) {
843 StringBuffer buf = new StringBuffer();
844 char[] chars = type.toCharArray();
845 boolean char_found = false, delim = false;
846 int index = -1;
847 loop: for (int i = 0; i < chars.length; i++) {
848 switch (chars[i]) {
849 case ' ':
850 case '\t':
851 case '\n':
852 case '\r':
853 case '\f':
854 if (char_found) {
855 delim = true;
856 }
857 break;
858 case '[':
859 if (!char_found) {
860 throw new RuntimeException("Illegal type: " + type);
861 }
862 index = i;
863 break loop;
864 default:
865 char_found = true;
866 if (!delim) {
867 buf.append(chars[i]);
868 }
869 }
870 }
871 int brackets = 0;
872 if (index > 0) {
873 brackets = countBrackets(type.substring(index));
874 }
875 type = buf.toString();
876 buf.setLength(0);
877 for (int i = 0; i < brackets; i++) {
878 buf.append('[');
879 }
880 boolean found = false;
881 for (int i = Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) {
882 if (Constants.TYPE_NAMES[i].equals(type)) {
883 found = true;
884 buf.append(Constants.SHORT_TYPE_NAMES[i]);
885 }
886 }
887 if (!found) {
888 buf.append('L').append(type.replace('.', '/')).append(';');
889 }
890 return buf.toString();
891 }
892
893
894 private static int countBrackets( String brackets ) {
895 char[] chars = brackets.toCharArray();
896 int count = 0;
897 boolean open = false;
898 for (int i = 0; i < chars.length; i++) {
899 switch (chars[i]) {
900 case '[':
901 if (open) {
902 throw new RuntimeException("Illegally nested brackets:" + brackets);
903 }
904 open = true;
905 break;
906 case ']':
907 if (!open) {
908 throw new RuntimeException("Illegally nested brackets:" + brackets);
909 }
910 open = false;
911 count++;
912 break;
913 default:
914
915 }
916 }
917 if (open) {
918 throw new RuntimeException("Illegally nested brackets:" + brackets);
919 }
920 return count;
921 }
922
923
924 /***
925 * Return type of method signature as a byte value as defined in <em>Constants</em>
926 *
927 * @param signature in format described above
928 * @return type of method signature
929 * @see Constants
930 */
931 public static final byte typeOfMethodSignature( String signature ) throws ClassFormatException {
932 int index;
933 try {
934 if (signature.charAt(0) != '(') {
935 throw new ClassFormatException("Invalid method signature: " + signature);
936 }
937 index = signature.lastIndexOf(')') + 1;
938 return typeOfSignature(signature.substring(index));
939 } catch (StringIndexOutOfBoundsException e) {
940 throw new ClassFormatException("Invalid method signature: " + signature);
941 }
942 }
943
944
945 /***
946 * Return type of signature as a byte value as defined in <em>Constants</em>
947 *
948 * @param signature in format described above
949 * @return type of signature
950 * @see Constants
951 */
952 public static final byte typeOfSignature( String signature ) throws ClassFormatException {
953 try {
954 switch (signature.charAt(0)) {
955 case 'B':
956 return Constants.T_BYTE;
957 case 'C':
958 return Constants.T_CHAR;
959 case 'D':
960 return Constants.T_DOUBLE;
961 case 'F':
962 return Constants.T_FLOAT;
963 case 'I':
964 return Constants.T_INT;
965 case 'J':
966 return Constants.T_LONG;
967 case 'L':
968 return Constants.T_REFERENCE;
969 case '[':
970 return Constants.T_ARRAY;
971 case 'V':
972 return Constants.T_VOID;
973 case 'Z':
974 return Constants.T_BOOLEAN;
975 case 'S':
976 return Constants.T_SHORT;
977 default:
978 throw new ClassFormatException("Invalid method signature: " + signature);
979 }
980 } catch (StringIndexOutOfBoundsException e) {
981 throw new ClassFormatException("Invalid method signature: " + signature);
982 }
983 }
984
985
986 /*** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
987 */
988 public static short searchOpcode( String name ) {
989 name = name.toLowerCase(Locale.ENGLISH);
990 for (short i = 0; i < Constants.OPCODE_NAMES.length; i++) {
991 if (Constants.OPCODE_NAMES[i].equals(name)) {
992 return i;
993 }
994 }
995 return -1;
996 }
997
998
999 /***
1000 * Convert (signed) byte to (unsigned) short value, i.e., all negative
1001 * values become positive.
1002 */
1003 private static final short byteToShort( byte b ) {
1004 return (b < 0) ? (short) (256 + b) : (short) b;
1005 }
1006
1007
1008 /*** Convert bytes into hexidecimal string
1009 *
1010 * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
1011 */
1012 public static final String toHexString( byte[] bytes ) {
1013 StringBuffer buf = new StringBuffer();
1014 for (int i = 0; i < bytes.length; i++) {
1015 short b = byteToShort(bytes[i]);
1016 String hex = Integer.toString(b, 0x10);
1017 if (b < 0x10) {
1018 buf.append('0');
1019 }
1020 buf.append(hex);
1021 if (i < bytes.length - 1) {
1022 buf.append(' ');
1023 }
1024 }
1025 return buf.toString();
1026 }
1027
1028
1029 /***
1030 * Return a string for an integer justified left or right and filled up with
1031 * `fill' characters if necessary.
1032 *
1033 * @param i integer to format
1034 * @param length length of desired string
1035 * @param left_justify format left or right
1036 * @param fill fill character
1037 * @return formatted int
1038 */
1039 public static final String format( int i, int length, boolean left_justify, char fill ) {
1040 return fillup(Integer.toString(i), length, left_justify, fill);
1041 }
1042
1043
1044 /***
1045 * Fillup char with up to length characters with char `fill' and justify it left or right.
1046 *
1047 * @param str string to format
1048 * @param length length of desired string
1049 * @param left_justify format left or right
1050 * @param fill fill character
1051 * @return formatted string
1052 */
1053 public static final String fillup( String str, int length, boolean left_justify, char fill ) {
1054 int len = length - str.length();
1055 char[] buf = new char[(len < 0) ? 0 : len];
1056 for (int j = 0; j < buf.length; j++) {
1057 buf[j] = fill;
1058 }
1059 if (left_justify) {
1060 return str + new String(buf);
1061 }
1062 return new String(buf) + str;
1063 }
1064
1065
1066 static final boolean equals( byte[] a, byte[] b ) {
1067 int size;
1068 if ((size = a.length) != b.length) {
1069 return false;
1070 }
1071 for (int i = 0; i < size; i++) {
1072 if (a[i] != b[i]) {
1073 return false;
1074 }
1075 }
1076 return true;
1077 }
1078
1079
1080 public static final void printArray( PrintStream out, Object[] obj ) {
1081 out.println(printArray(obj, true));
1082 }
1083
1084
1085 public static final void printArray( PrintWriter out, Object[] obj ) {
1086 out.println(printArray(obj, true));
1087 }
1088
1089
1090 public static final String printArray( Object[] obj ) {
1091 return printArray(obj, true);
1092 }
1093
1094
1095 public static final String printArray( Object[] obj, boolean braces ) {
1096 return printArray(obj, braces, false);
1097 }
1098
1099
1100 public static final String printArray( Object[] obj, boolean braces, boolean quote ) {
1101 if (obj == null) {
1102 return null;
1103 }
1104 StringBuffer buf = new StringBuffer();
1105 if (braces) {
1106 buf.append('{');
1107 }
1108 for (int i = 0; i < obj.length; i++) {
1109 if (obj[i] != null) {
1110 buf.append((quote ? "\"" : "")).append(obj[i].toString()).append(
1111 (quote ? "\"" : ""));
1112 } else {
1113 buf.append("null");
1114 }
1115 if (i < obj.length - 1) {
1116 buf.append(", ");
1117 }
1118 }
1119 if (braces) {
1120 buf.append('}');
1121 }
1122 return buf.toString();
1123 }
1124
1125
1126 /*** @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
1127 */
1128 public static boolean isJavaIdentifierPart( char ch ) {
1129 return ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))
1130 || ((ch >= '0') && (ch <= '9')) || (ch == '_');
1131 }
1132
1133
1134 /*** Encode byte array it into Java identifier string, i.e., a string
1135 * that only contains the following characters: (a, ... z, A, ... Z,
1136 * 0, ... 9, _, $). The encoding algorithm itself is not too
1137 * clever: if the current byte's ASCII value already is a valid Java
1138 * identifier part, leave it as it is. Otherwise it writes the
1139 * escape character($) followed by <p><ul><li> the ASCII value as a
1140 * hexadecimal string, if the value is not in the range
1141 * 200..247</li> <li>a Java identifier char not used in a lowercase
1142 * hexadecimal string, if the value is in the range
1143 * 200..247</li><ul></p>
1144 *
1145 * <p>This operation inflates the original byte array by roughly 40-50%</p>
1146 *
1147 * @param bytes the byte array to convert
1148 * @param compress use gzip to minimize string
1149 */
1150 public static String encode( byte[] bytes, boolean compress ) throws IOException {
1151 if (compress) {
1152 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1153 GZIPOutputStream gos = new GZIPOutputStream(baos);
1154 gos.write(bytes, 0, bytes.length);
1155 gos.close();
1156 baos.close();
1157 bytes = baos.toByteArray();
1158 }
1159 CharArrayWriter caw = new CharArrayWriter();
1160 JavaWriter jw = new JavaWriter(caw);
1161 for (int i = 0; i < bytes.length; i++) {
1162 int in = bytes[i] & 0x000000ff;
1163 jw.write(in);
1164 }
1165 return caw.toString();
1166 }
1167
1168
1169 /*** Decode a string back to a byte array.
1170 *
1171 * @param s the string to convert
1172 * @param uncompress use gzip to uncompress the stream of bytes
1173 */
1174 public static byte[] decode( String s, boolean uncompress ) throws IOException {
1175 char[] chars = s.toCharArray();
1176 CharArrayReader car = new CharArrayReader(chars);
1177 JavaReader jr = new JavaReader(car);
1178 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1179 int ch;
1180 while ((ch = jr.read()) >= 0) {
1181 bos.write(ch);
1182 }
1183 bos.close();
1184 car.close();
1185 jr.close();
1186 byte[] bytes = bos.toByteArray();
1187 if (uncompress) {
1188 GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
1189 byte[] tmp = new byte[bytes.length * 3];
1190 int count = 0;
1191 int b;
1192 while ((b = gis.read()) >= 0) {
1193 tmp[count++] = (byte) b;
1194 }
1195 bytes = new byte[count];
1196 System.arraycopy(tmp, 0, bytes, 0, count);
1197 }
1198 return bytes;
1199 }
1200
1201
1202 private static final int FREE_CHARS = 48;
1203 static int[] CHAR_MAP = new int[FREE_CHARS];
1204 static int[] MAP_CHAR = new int[256];
1205 private static final char ESCAPE_CHAR = '$';
1206 static {
1207 int j = 0;
1208 for (int i = 'A'; i <= 'Z'; i++) {
1209 CHAR_MAP[j] = i;
1210 MAP_CHAR[i] = j;
1211 j++;
1212 }
1213 for (int i = 'g'; i <= 'z'; i++) {
1214 CHAR_MAP[j] = i;
1215 MAP_CHAR[i] = j;
1216 j++;
1217 }
1218 CHAR_MAP[j] = '$';
1219 MAP_CHAR['$'] = j;
1220 j++;
1221 CHAR_MAP[j] = '_';
1222 MAP_CHAR['_'] = j;
1223 }
1224
1225 /*** Decode characters into bytes.
1226 * Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
1227 */
1228 private static class JavaReader extends FilterReader {
1229
1230 public JavaReader(Reader in) {
1231 super(in);
1232 }
1233
1234
1235 public int read() throws IOException {
1236 int b = in.read();
1237 if (b != ESCAPE_CHAR) {
1238 return b;
1239 }
1240 int i = in.read();
1241 if (i < 0) {
1242 return -1;
1243 }
1244 if (((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) {
1245 int j = in.read();
1246 if (j < 0) {
1247 return -1;
1248 }
1249 char[] tmp = {
1250 (char) i, (char) j
1251 };
1252 int s = Integer.parseInt(new String(tmp), 16);
1253 return s;
1254 }
1255 return MAP_CHAR[i];
1256 }
1257
1258
1259 public int read( char[] cbuf, int off, int len ) throws IOException {
1260 for (int i = 0; i < len; i++) {
1261 cbuf[off + i] = (char) read();
1262 }
1263 return len;
1264 }
1265 }
1266
1267 /*** Encode bytes into valid java identifier characters.
1268 * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a>
1269 */
1270 private static class JavaWriter extends FilterWriter {
1271
1272 public JavaWriter(Writer out) {
1273 super(out);
1274 }
1275
1276
1277 public void write( int b ) throws IOException {
1278 if (isJavaIdentifierPart((char) b) && (b != ESCAPE_CHAR)) {
1279 out.write(b);
1280 } else {
1281 out.write(ESCAPE_CHAR);
1282
1283 if (b >= 0 && b < FREE_CHARS) {
1284 out.write(CHAR_MAP[b]);
1285 } else {
1286 char[] tmp = Integer.toHexString(b).toCharArray();
1287 if (tmp.length == 1) {
1288 out.write('0');
1289 out.write(tmp[0]);
1290 } else {
1291 out.write(tmp[0]);
1292 out.write(tmp[1]);
1293 }
1294 }
1295 }
1296 }
1297
1298
1299 public void write( char[] cbuf, int off, int len ) throws IOException {
1300 for (int i = 0; i < len; i++) {
1301 write(cbuf[off + i]);
1302 }
1303 }
1304
1305
1306 public void write( String str, int off, int len ) throws IOException {
1307 write(str.toCharArray(), off, len);
1308 }
1309 }
1310
1311
1312 /***
1313 * Escape all occurences of newline chars '\n', quotes \", etc.
1314 */
1315 public static final String convertString( String label ) {
1316 char[] ch = label.toCharArray();
1317 StringBuffer buf = new StringBuffer();
1318 for (int i = 0; i < ch.length; i++) {
1319 switch (ch[i]) {
1320 case '\n':
1321 buf.append("//n");
1322 break;
1323 case '\r':
1324 buf.append("//r");
1325 break;
1326 case '\"':
1327 buf.append("//\"");
1328 break;
1329 case '\'':
1330 buf.append("//'");
1331 break;
1332 case '//':
1333 buf.append("////");
1334 break;
1335 default:
1336 buf.append(ch[i]);
1337 break;
1338 }
1339 }
1340 return buf.toString();
1341 }
1342 }