1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.bcel.classfile;
21
22 import java.io.ByteArrayInputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.CharArrayReader;
25 import java.io.CharArrayWriter;
26 import java.io.FilterReader;
27 import java.io.FilterWriter;
28 import java.io.IOException;
29 import java.io.PrintStream;
30 import java.io.PrintWriter;
31 import java.io.Reader;
32 import java.io.Writer;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.zip.GZIPInputStream;
37 import java.util.zip.GZIPOutputStream;
38
39 import org.apache.bcel.Const;
40 import org.apache.bcel.util.ByteSequence;
41 import org.apache.commons.lang3.ArrayFill;
42 import org.apache.commons.lang3.ArrayUtils;
43 import org.apache.commons.lang3.StringUtils;
44
45
46
47
48
49 public abstract class Utility {
50
51
52
53
54 private static final class JavaReader extends FilterReader {
55
56 JavaReader(final Reader in) {
57 super(in);
58 }
59
60 @Override
61 public int read() throws IOException {
62 final int b = in.read();
63 if (b != ESCAPE_CHAR) {
64 return b;
65 }
66 final int i = in.read();
67 if (i < 0) {
68 return -1;
69 }
70 if (i >= '0' && i <= '9' || i >= 'a' && i <= 'f') {
71 final int j = in.read();
72 if (j < 0) {
73 return -1;
74 }
75 final char[] tmp = {(char) i, (char) j};
76 return Integer.parseInt(new String(tmp), 16);
77 }
78 return MAP_CHAR[i];
79 }
80
81 @Override
82 public int read(final char[] cbuf, final int off, final int len) throws IOException {
83 for (int i = 0; i < len; i++) {
84 cbuf[off + i] = (char) read();
85 }
86 return len;
87 }
88 }
89
90
91
92
93
94 private static final class JavaWriter extends FilterWriter {
95
96 JavaWriter(final Writer out) {
97 super(out);
98 }
99
100 @Override
101 public void write(final char[] cbuf, final int off, final int len) throws IOException {
102 for (int i = 0; i < len; i++) {
103 write(cbuf[off + i]);
104 }
105 }
106
107 @Override
108 public void write(final int b) throws IOException {
109 if (isJavaIdentifierPart((char) b) && b != ESCAPE_CHAR) {
110 out.write(b);
111 } else {
112 out.write(ESCAPE_CHAR);
113
114 if (b >= 0 && b < FREE_CHARS) {
115 out.write(CHAR_MAP[b]);
116 } else {
117 final char[] tmp = Integer.toHexString(b).toCharArray();
118 if (tmp.length == 1) {
119 out.write('0');
120 out.write(tmp[0]);
121 } else {
122 out.write(tmp[0]);
123 out.write(tmp[1]);
124 }
125 }
126 }
127 }
128
129 @Override
130 public void write(final String str, final int off, final int len) throws IOException {
131 write(str.toCharArray(), off, len);
132 }
133 }
134
135
136
137
138
139 private static final ThreadLocal<Integer> CONSUMER_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0));
140
141
142
143
144
145
146 private static boolean wide;
147
148
149 private static final int FREE_CHARS = 48;
150
151 private static final int[] CHAR_MAP = new int[FREE_CHARS];
152
153 private static final int[] MAP_CHAR = new int[256];
154
155 private static final char ESCAPE_CHAR = '$';
156
157 static {
158 int j = 0;
159 for (int i = 'A'; i <= 'Z'; i++) {
160 CHAR_MAP[j] = i;
161 MAP_CHAR[i] = j;
162 j++;
163 }
164 for (int i = 'g'; i <= 'z'; i++) {
165 CHAR_MAP[j] = i;
166 MAP_CHAR[i] = j;
167 j++;
168 }
169 CHAR_MAP[j] = '$';
170 MAP_CHAR['$'] = j;
171 j++;
172 CHAR_MAP[j] = '_';
173 MAP_CHAR['_'] = j;
174 }
175
176
177
178
179
180
181
182 public static String accessToString(final int accessFlags) {
183 return accessToString(accessFlags, false);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197 public static String accessToString(final int accessFlags, final boolean forClass) {
198 final StringBuilder buf = new StringBuilder();
199 int p = 0;
200 for (int i = 0; p < Const.MAX_ACC_FLAG_I; i++) {
201 p = pow2(i);
202 if ((accessFlags & p) != 0) {
203
204
205
206
207 if (forClass && (p == Const.ACC_SUPER || p == Const.ACC_INTERFACE)) {
208 continue;
209 }
210 buf.append(Const.getAccessName(i)).append(" ");
211 }
212 }
213 return buf.toString().trim();
214 }
215
216
217
218
219 private static short byteToShort(final byte b) {
220 return b < 0 ? (short) (256 + b) : (short) b;
221 }
222
223
224
225
226
227 public static String classOrInterface(final int accessFlags) {
228 return (accessFlags & Const.ACC_INTERFACE) != 0 ? "interface" : "class";
229 }
230
231
232
233
234 public static int clearBit(final int flag, final int i) {
235 final int bit = pow2(i);
236 return (flag & bit) == 0 ? flag : flag ^ bit;
237 }
238
239 public static String codeToString(final byte[] code, final ConstantPool constantPool, final int index, final int length) {
240 return codeToString(code, constantPool, index, length, true);
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254 public static String codeToString(final byte[] code, final ConstantPool constantPool, final int index, final int length, final boolean verbose) {
255 final StringBuilder buf = new StringBuilder(code.length * 20);
256 try (ByteSequence stream = new ByteSequence(code)) {
257 for (int i = 0; i < index; i++) {
258 codeToString(stream, constantPool, verbose);
259 }
260 for (int i = 0; stream.available() > 0; i++) {
261 if (length < 0 || i < length) {
262 final String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
263 buf.append(indices).append(codeToString(stream, constantPool, verbose)).append('\n');
264 }
265 }
266 } catch (final IOException e) {
267 throw new ClassFormatException("Byte code error: " + buf.toString(), e);
268 }
269 return buf.toString();
270 }
271
272 public static String codeToString(final ByteSequence bytes, final ConstantPool constantPool) throws IOException {
273 return codeToString(bytes, constantPool, true);
274 }
275
276
277
278
279
280
281
282
283
284
285 public static String codeToString(final ByteSequence bytes, final ConstantPool constantPool, final boolean verbose) throws IOException {
286 final short opcode = (short) bytes.readUnsignedByte();
287 int defaultOffset = 0;
288 final int low;
289 final int high;
290 final int npairs;
291 final int index;
292 final int vindex;
293 final int constant;
294 final int[] match;
295 final int[] jumpTable;
296 int noPadBytes = 0;
297 final int offset;
298 final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode));
299
300
301
302 if (opcode == Const.TABLESWITCH || opcode == Const.LOOKUPSWITCH) {
303 final int remainder = bytes.getIndex() % 4;
304 noPadBytes = remainder == 0 ? 0 : 4 - remainder;
305 for (int i = 0; i < noPadBytes; i++) {
306 final byte b;
307 if ((b = bytes.readByte()) != 0) {
308 System.err.println("Warning: Padding byte != 0 in " + Const.getOpcodeName(opcode) + ":" + b);
309 }
310 }
311
312 defaultOffset = bytes.readInt();
313 }
314 switch (opcode) {
315
316
317
318 case Const.TABLESWITCH:
319 low = bytes.readInt();
320 high = bytes.readInt();
321 offset = bytes.getIndex() - 12 - noPadBytes - 1;
322 defaultOffset += offset;
323 buf.append("\tdefault = ").append(defaultOffset).append(", low = ").append(low).append(", high = ").append(high).append("(");
324 jumpTable = new int[high - low + 1];
325 for (int i = 0; i < jumpTable.length; i++) {
326 jumpTable[i] = offset + bytes.readInt();
327 buf.append(jumpTable[i]);
328 if (i < jumpTable.length - 1) {
329 buf.append(", ");
330 }
331 }
332 buf.append(")");
333 break;
334
335
336
337 case Const.LOOKUPSWITCH: {
338 npairs = bytes.readInt();
339 offset = bytes.getIndex() - 8 - noPadBytes - 1;
340 match = new int[npairs];
341 jumpTable = new int[npairs];
342 defaultOffset += offset;
343 buf.append("\tdefault = ").append(defaultOffset).append(", npairs = ").append(npairs).append(" (");
344 for (int i = 0; i < npairs; i++) {
345 match[i] = bytes.readInt();
346 jumpTable[i] = offset + bytes.readInt();
347 buf.append("(").append(match[i]).append(", ").append(jumpTable[i]).append(")");
348 if (i < npairs - 1) {
349 buf.append(", ");
350 }
351 }
352 buf.append(")");
353 }
354 break;
355
356
357
358 case Const.GOTO:
359 case Const.IFEQ:
360 case Const.IFGE:
361 case Const.IFGT:
362 case Const.IFLE:
363 case Const.IFLT:
364 case Const.JSR:
365 case Const.IFNE:
366 case Const.IFNONNULL:
367 case Const.IFNULL:
368 case Const.IF_ACMPEQ:
369 case Const.IF_ACMPNE:
370 case Const.IF_ICMPEQ:
371 case Const.IF_ICMPGE:
372 case Const.IF_ICMPGT:
373 case Const.IF_ICMPLE:
374 case Const.IF_ICMPLT:
375 case Const.IF_ICMPNE:
376 buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readShort());
377 break;
378
379
380
381 case Const.GOTO_W:
382 case Const.JSR_W:
383 buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readInt());
384 break;
385
386
387
388 case Const.ALOAD:
389 case Const.ASTORE:
390 case Const.DLOAD:
391 case Const.DSTORE:
392 case Const.FLOAD:
393 case Const.FSTORE:
394 case Const.ILOAD:
395 case Const.ISTORE:
396 case Const.LLOAD:
397 case Const.LSTORE:
398 case Const.RET:
399 if (wide) {
400 vindex = bytes.readUnsignedShort();
401 wide = false;
402 } else {
403 vindex = bytes.readUnsignedByte();
404 }
405 buf.append("\t\t%").append(vindex);
406 break;
407
408
409
410
411 case Const.WIDE:
412 wide = true;
413 buf.append("\t(wide)");
414 break;
415
416
417
418 case Const.NEWARRAY:
419 buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">");
420 break;
421
422
423
424 case Const.GETFIELD:
425 case Const.GETSTATIC:
426 case Const.PUTFIELD:
427 case Const.PUTSTATIC:
428 index = bytes.readUnsignedShort();
429 buf.append("\t\t").append(constantPool.constantToString(index, Const.CONSTANT_Fieldref)).append(verbose ? " (" + index + ")" : "");
430 break;
431
432
433
434 case Const.NEW:
435 case Const.CHECKCAST:
436 buf.append("\t");
437 index = bytes.readUnsignedShort();
438 buf.append("\t<").append(constantPool.constantToString(index, Const.CONSTANT_Class)).append(">").append(verbose ? " (" + index + ")" : "");
439 break;
440 case Const.INSTANCEOF:
441 index = bytes.readUnsignedShort();
442 buf.append("\t<").append(constantPool.constantToString(index, Const.CONSTANT_Class)).append(">").append(verbose ? " (" + index + ")" : "");
443 break;
444
445
446
447 case Const.INVOKESPECIAL:
448 case Const.INVOKESTATIC:
449 index = bytes.readUnsignedShort();
450 final Constant c = constantPool.getConstant(index);
451
452
453 buf.append("\t").append(constantPool.constantToString(index, c.getTag())).append(verbose ? " (" + index + ")" : "");
454 break;
455 case Const.INVOKEVIRTUAL:
456 index = bytes.readUnsignedShort();
457 buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_Methodref)).append(verbose ? " (" + index + ")" : "");
458 break;
459 case Const.INVOKEINTERFACE:
460 index = bytes.readUnsignedShort();
461 final int nargs = bytes.readUnsignedByte();
462 buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_InterfaceMethodref)).append(verbose ? " (" + index + ")\t" : "")
463 .append(nargs).append("\t").append(bytes.readUnsignedByte());
464 break;
465 case Const.INVOKEDYNAMIC:
466 index = bytes.readUnsignedShort();
467 buf.append("\t").append(constantPool.constantToString(index, Const.CONSTANT_InvokeDynamic)).append(verbose ? " (" + index + ")\t" : "")
468 .append(bytes.readUnsignedByte())
469 .append(bytes.readUnsignedByte());
470 break;
471
472
473
474 case Const.LDC_W:
475 case Const.LDC2_W:
476 index = bytes.readUnsignedShort();
477 buf.append("\t\t").append(constantPool.constantToString(index, constantPool.getConstant(index).getTag()))
478 .append(verbose ? " (" + index + ")" : "");
479 break;
480 case Const.LDC:
481 index = bytes.readUnsignedByte();
482 buf.append("\t\t").append(constantPool.constantToString(index, constantPool.getConstant(index).getTag()))
483 .append(verbose ? " (" + index + ")" : "");
484 break;
485
486
487
488 case Const.ANEWARRAY:
489 index = bytes.readUnsignedShort();
490 buf.append("\t\t<").append(compactClassName(constantPool.getConstantString(index, Const.CONSTANT_Class), false)).append(">")
491 .append(verbose ? " (" + index + ")" : "");
492 break;
493
494
495
496 case Const.MULTIANEWARRAY: {
497 index = bytes.readUnsignedShort();
498 final int dimensions = bytes.readUnsignedByte();
499 buf.append("\t<").append(compactClassName(constantPool.getConstantString(index, Const.CONSTANT_Class), false)).append(">\t").append(dimensions)
500 .append(verbose ? " (" + index + ")" : "");
501 }
502 break;
503
504
505
506 case Const.IINC:
507 if (wide) {
508 vindex = bytes.readUnsignedShort();
509 constant = bytes.readShort();
510 wide = false;
511 } else {
512 vindex = bytes.readUnsignedByte();
513 constant = bytes.readByte();
514 }
515 buf.append("\t\t%").append(vindex).append("\t").append(constant);
516 break;
517 default:
518 if (Const.getNoOfOperands(opcode) > 0) {
519 for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
520 buf.append("\t\t");
521 switch (Const.getOperandType(opcode, i)) {
522 case Const.T_BYTE:
523 buf.append(bytes.readByte());
524 break;
525 case Const.T_SHORT:
526 buf.append(bytes.readShort());
527 break;
528 case Const.T_INT:
529 buf.append(bytes.readInt());
530 break;
531 default:
532 throw new IllegalStateException("Unreachable default case reached!");
533 }
534 }
535 }
536 }
537 return buf.toString();
538 }
539
540
541
542
543
544
545
546 public static String compactClassName(final String str) {
547 return compactClassName(str, true);
548 }
549
550
551
552
553
554
555
556
557
558 public static String compactClassName(final String str, final boolean chopit) {
559 return compactClassName(str, "java.lang.", chopit);
560 }
561
562
563
564
565
566
567
568
569
570
571 public static String compactClassName(String str, final String prefix, final boolean chopit) {
572 final int len = prefix.length();
573 str = pathToPackage(str);
574
575 if (chopit && str.startsWith(prefix) && str.substring(len).indexOf('.') == -1) {
576 str = str.substring(len);
577 }
578 return str;
579 }
580
581
582
583
584 public static String convertString(final String label) {
585 final char[] ch = label.toCharArray();
586 final StringBuilder buf = new StringBuilder();
587 for (final char element : ch) {
588 switch (element) {
589 case '\n':
590 buf.append("\\n");
591 break;
592 case '\r':
593 buf.append("\\r");
594 break;
595 case '\"':
596 buf.append("\\\"");
597 break;
598 case '\'':
599 buf.append("\\'");
600 break;
601 case '\\':
602 buf.append("\\\\");
603 break;
604 default:
605 buf.append(element);
606 break;
607 }
608 }
609 return buf.toString();
610 }
611
612 private static int countBrackets(final String brackets) {
613 final char[] chars = brackets.toCharArray();
614 int count = 0;
615 boolean open = false;
616 for (final char c : chars) {
617 switch (c) {
618 case '[':
619 if (open) {
620 throw new IllegalArgumentException("Illegally nested brackets:" + brackets);
621 }
622 open = true;
623 break;
624 case ']':
625 if (!open) {
626 throw new IllegalArgumentException("Illegally nested brackets:" + brackets);
627 }
628 open = false;
629 count++;
630 break;
631 default:
632
633 break;
634 }
635 }
636 if (open) {
637 throw new IllegalArgumentException("Illegally nested brackets:" + brackets);
638 }
639 return count;
640 }
641
642
643
644
645
646
647
648
649 public static byte[] decode(final String s, final boolean uncompress) throws IOException {
650 byte[] bytes;
651 try (JavaReader jr = new JavaReader(new CharArrayReader(s.toCharArray())); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
652 int ch;
653 while ((ch = jr.read()) >= 0) {
654 bos.write(ch);
655 }
656 bytes = bos.toByteArray();
657 }
658 if (uncompress) {
659 final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
660 final byte[] tmp = new byte[bytes.length * 3];
661 int count = 0;
662 int b;
663 while ((b = gis.read()) >= 0) {
664 tmp[count++] = (byte) b;
665 }
666 bytes = Arrays.copyOf(tmp, count);
667 }
668 return bytes;
669 }
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689 public static String encode(byte[] bytes, final boolean compress) throws IOException {
690 if (compress) {
691 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos)) {
692 gos.write(bytes, 0, bytes.length);
693 gos.close();
694 bytes = baos.toByteArray();
695 }
696 }
697 final CharArrayWriter caw = new CharArrayWriter();
698 try (JavaWriter jw = new JavaWriter(caw)) {
699 for (final byte b : bytes) {
700 final int in = b & 0x000000ff;
701 jw.write(in);
702 }
703 }
704 return caw.toString();
705 }
706
707
708
709
710
711
712
713
714
715
716 public static String fillup(final String str, final int length, final boolean leftJustify, final char fill) {
717 final int len = length - str.length();
718 final char[] buf = ArrayFill.fill(new char[Math.max(len, 0)], fill);
719 if (leftJustify) {
720 return str + new String(buf);
721 }
722 return new String(buf) + str;
723 }
724
725
726
727
728
729
730
731
732
733
734 public static String format(final int i, final int length, final boolean leftJustify, final char fill) {
735 return fillup(Integer.toString(i), length, leftJustify, fill);
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769 public static String getSignature(String type) {
770 final StringBuilder buf = new StringBuilder();
771 final char[] chars = type.toCharArray();
772 boolean charFound = false;
773 boolean delim = false;
774 int index = -1;
775 loop: for (int i = 0; i < chars.length; i++) {
776 switch (chars[i]) {
777 case ' ':
778 case '\t':
779 case '\n':
780 case '\r':
781 case '\f':
782 if (charFound) {
783 delim = true;
784 }
785 break;
786 case '[':
787 if (!charFound) {
788 throw new IllegalArgumentException("Illegal type: " + type);
789 }
790 index = i;
791 break loop;
792 default:
793 charFound = true;
794 if (!delim) {
795 buf.append(chars[i]);
796 }
797 }
798 }
799 int brackets = 0;
800 if (index > 0) {
801 brackets = countBrackets(type.substring(index));
802 }
803 type = buf.toString();
804 buf.setLength(0);
805 for (int i = 0; i < brackets; i++) {
806 buf.append('[');
807 }
808 boolean found = false;
809 for (int i = Const.T_BOOLEAN; i <= Const.T_VOID && !found; i++) {
810 if (Const.getTypeName(i).equals(type)) {
811 found = true;
812 buf.append(Const.getShortTypeName(i));
813 }
814 }
815 if (!found) {
816 buf.append('L').append(packageToPath(type)).append(';');
817 }
818 return buf.toString();
819 }
820
821
822
823
824
825 public static boolean isJavaIdentifierPart(final char ch) {
826 return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == '_';
827 }
828
829
830
831
832 public static boolean isSet(final int flag, final int i) {
833 return (flag & pow2(i)) != 0;
834 }
835
836
837
838
839
840
841
842
843 public static String[] methodSignatureArgumentTypes(final String signature) throws ClassFormatException {
844 return methodSignatureArgumentTypes(signature, true);
845 }
846
847
848
849
850
851
852
853
854
855 public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit) throws ClassFormatException {
856 final List<String> vec = new ArrayList<>();
857 int index;
858 try {
859
860 index = signature.indexOf('(') + 1;
861 if (index <= 0) {
862 throw new InvalidMethodSignatureException(signature);
863 }
864 while (signature.charAt(index) != ')') {
865 vec.add(typeSignatureToString(signature.substring(index), chopit));
866
867 index += unwrap(CONSUMER_CHARS);
868 }
869 } catch (final StringIndexOutOfBoundsException e) {
870 throw new InvalidMethodSignatureException(signature, e);
871 }
872 return vec.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
873 }
874
875
876
877
878
879
880
881
882 public static String methodSignatureReturnType(final String signature) throws ClassFormatException {
883 return methodSignatureReturnType(signature, true);
884 }
885
886
887
888
889
890
891
892
893
894 public static String methodSignatureReturnType(final String signature, final boolean chopit) throws ClassFormatException {
895 try {
896
897 final int index = signature.lastIndexOf(')') + 1;
898 if (index <= 0) {
899 throw new InvalidMethodSignatureException(signature);
900 }
901 return typeSignatureToString(signature.substring(index), chopit);
902 } catch (final StringIndexOutOfBoundsException e) {
903 throw new InvalidMethodSignatureException(signature, e);
904 }
905 }
906
907
908
909
910
911
912
913
914
915 public static String methodSignatureToString(final String signature, final String name, final String access) {
916 return methodSignatureToString(signature, name, access, true);
917 }
918
919
920
921
922
923
924
925
926
927
928 public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit) {
929 return methodSignatureToString(signature, name, access, chopit, null);
930 }
931
932
933
934
935
936
937
938
939
940
941
942
943
944 public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit,
945 final LocalVariableTable vars) throws ClassFormatException {
946 final StringBuilder buf = new StringBuilder("(");
947 final String type;
948 int index;
949 int varIndex = access.contains("static") ? 0 : 1;
950 try {
951
952 index = signature.indexOf('(') + 1;
953 if (index <= 0) {
954 throw new InvalidMethodSignatureException(signature);
955 }
956 while (signature.charAt(index) != ')') {
957 final String paramType = typeSignatureToString(signature.substring(index), chopit);
958 buf.append(paramType);
959 if (vars != null) {
960 final LocalVariable l = vars.getLocalVariable(varIndex, 0);
961 if (l != null) {
962 buf.append(" ").append(l.getName());
963 }
964 } else {
965 buf.append(" arg").append(varIndex);
966 }
967 if ("double".equals(paramType) || "long".equals(paramType)) {
968 varIndex += 2;
969 } else {
970 varIndex++;
971 }
972 buf.append(", ");
973
974 index += unwrap(CONSUMER_CHARS);
975 }
976 index++;
977
978 type = typeSignatureToString(signature.substring(index), chopit);
979 } catch (final StringIndexOutOfBoundsException e) {
980 throw new InvalidMethodSignatureException(signature, e);
981 }
982
983 if (buf.length() > 1) {
984 buf.setLength(buf.length() - 2);
985 }
986 buf.append(")");
987 return access + (!access.isEmpty() ? " " : "") +
988 type + " " + name + buf.toString();
989 }
990
991
992
993
994
995
996
997
998
999 public static String methodTypeToSignature(final String ret, final String[] argv) throws ClassFormatException {
1000 final StringBuilder buf = new StringBuilder("(");
1001 String str;
1002 if (argv != null) {
1003 for (final String element : argv) {
1004 str = getSignature(element);
1005 if (str.endsWith("V")) {
1006 throw new ClassFormatException("Invalid type: " + element);
1007 }
1008 buf.append(str);
1009 }
1010 }
1011 str = getSignature(ret);
1012 buf.append(")").append(str);
1013 return buf.toString();
1014 }
1015
1016
1017
1018
1019
1020
1021
1022
1023 public static String packageToPath(final String name) {
1024 return name.replace('.', '/');
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034 public static String pathToPackage(final String str) {
1035 return str.replace('/', '.');
1036 }
1037
1038 private static int pow2(final int n) {
1039 return 1 << n;
1040 }
1041
1042 public static String printArray(final Object[] obj) {
1043 return printArray(obj, true);
1044 }
1045
1046 public static String printArray(final Object[] obj, final boolean braces) {
1047 return printArray(obj, braces, false);
1048 }
1049
1050 public static String printArray(final Object[] obj, final boolean braces, final boolean quote) {
1051 if (obj == null) {
1052 return null;
1053 }
1054 final StringBuilder buf = new StringBuilder();
1055 if (braces) {
1056 buf.append('{');
1057 }
1058 for (int i = 0; i < obj.length; i++) {
1059 if (obj[i] != null) {
1060 buf.append(quote ? "\"" : "").append(obj[i]).append(quote ? "\"" : "");
1061 } else {
1062 buf.append("null");
1063 }
1064 if (i < obj.length - 1) {
1065 buf.append(", ");
1066 }
1067 }
1068 if (braces) {
1069 buf.append('}');
1070 }
1071 return buf.toString();
1072 }
1073
1074 public static void printArray(final PrintStream out, final Object[] obj) {
1075 out.println(printArray(obj, true));
1076 }
1077
1078 public static void printArray(final PrintWriter out, final Object[] obj) {
1079 out.println(printArray(obj, true));
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090 public static String replace(String str, final String old, final String new_) {
1091 int index;
1092 int oldIndex;
1093 try {
1094 if (str.contains(old)) {
1095 final StringBuilder buf = new StringBuilder();
1096 oldIndex = 0;
1097
1098 while ((index = str.indexOf(old, oldIndex)) != -1) {
1099 buf.append(str, oldIndex, index);
1100 buf.append(new_);
1101 oldIndex = index + old.length();
1102 }
1103 buf.append(str.substring(oldIndex));
1104 str = buf.toString();
1105 }
1106 } catch (final StringIndexOutOfBoundsException e) {
1107 System.err.println(e);
1108 }
1109 return str;
1110 }
1111
1112
1113
1114
1115
1116
1117
1118 public static short searchOpcode(final String name) {
1119 final String lcName = StringUtils.toRootLowerCase(name);
1120 for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) {
1121 if (Const.getOpcodeName(i).equals(lcName)) {
1122 return i;
1123 }
1124 }
1125 return -1;
1126 }
1127
1128
1129
1130
1131 public static int setBit(final int flag, final int i) {
1132 return flag | pow2(i);
1133 }
1134
1135
1136
1137
1138
1139
1140
1141
1142 public static String signatureToString(final String signature) {
1143 return signatureToString(signature, true);
1144 }
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154 public static String signatureToString(final String signature, final boolean chopit) {
1155 String type = "";
1156 String typeParams = "";
1157 int index = 0;
1158 if (signature.charAt(0) == '<') {
1159
1160 typeParams = typeParamTypesToString(signature, chopit);
1161 index += unwrap(CONSUMER_CHARS);
1162 }
1163 if (signature.charAt(index) == '(') {
1164
1165
1166 type = typeParams + typeSignaturesToString(signature.substring(index), chopit, ')');
1167 index += unwrap(CONSUMER_CHARS);
1168
1169 type += typeSignatureToString(signature.substring(index), chopit);
1170 index += unwrap(CONSUMER_CHARS);
1171
1172 return type;
1173 }
1174
1175 type = typeSignatureToString(signature.substring(index), chopit);
1176 index += unwrap(CONSUMER_CHARS);
1177 if (typeParams.isEmpty() && index == signature.length()) {
1178
1179 return type;
1180 }
1181
1182 final StringBuilder typeClass = new StringBuilder(typeParams);
1183 typeClass.append(" extends ");
1184 typeClass.append(type);
1185 if (index < signature.length()) {
1186 typeClass.append(" implements ");
1187 typeClass.append(typeSignatureToString(signature.substring(index), chopit));
1188 index += unwrap(CONSUMER_CHARS);
1189 }
1190 while (index < signature.length()) {
1191 typeClass.append(", ");
1192 typeClass.append(typeSignatureToString(signature.substring(index), chopit));
1193 index += unwrap(CONSUMER_CHARS);
1194 }
1195 return typeClass.toString();
1196 }
1197
1198
1199
1200
1201
1202
1203
1204 public static String toHexString(final byte[] bytes) {
1205 final StringBuilder buf = new StringBuilder();
1206 for (int i = 0; i < bytes.length; i++) {
1207 final short b = byteToShort(bytes[i]);
1208 final String hex = Integer.toHexString(b);
1209 if (b < 0x10) {
1210 buf.append('0');
1211 }
1212 buf.append(hex);
1213 if (i < bytes.length - 1) {
1214 buf.append(' ');
1215 }
1216 }
1217 return buf.toString();
1218 }
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228 public static byte typeOfMethodSignature(final String signature) throws ClassFormatException {
1229 try {
1230 if (signature.charAt(0) != '(') {
1231 throw new InvalidMethodSignatureException(signature);
1232 }
1233 final int index = signature.lastIndexOf(')') + 1;
1234 return typeOfSignature(signature.substring(index));
1235 } catch (final StringIndexOutOfBoundsException e) {
1236 throw new InvalidMethodSignatureException(signature, e);
1237 }
1238 }
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248 public static byte typeOfSignature(final String signature) throws ClassFormatException {
1249 try {
1250 switch (signature.charAt(0)) {
1251 case 'B':
1252 return Const.T_BYTE;
1253 case 'C':
1254 return Const.T_CHAR;
1255 case 'D':
1256 return Const.T_DOUBLE;
1257 case 'F':
1258 return Const.T_FLOAT;
1259 case 'I':
1260 return Const.T_INT;
1261 case 'J':
1262 return Const.T_LONG;
1263 case 'L':
1264 case 'T':
1265 return Const.T_REFERENCE;
1266 case '[':
1267 return Const.T_ARRAY;
1268 case 'V':
1269 return Const.T_VOID;
1270 case 'Z':
1271 return Const.T_BOOLEAN;
1272 case 'S':
1273 return Const.T_SHORT;
1274 case '!':
1275 case '+':
1276 case '*':
1277 return typeOfSignature(signature.substring(1));
1278 default:
1279 throw new InvalidMethodSignatureException(signature);
1280 }
1281 } catch (final StringIndexOutOfBoundsException e) {
1282 throw new InvalidMethodSignatureException(signature, e);
1283 }
1284 }
1285
1286
1287
1288
1289
1290
1291
1292
1293 private static String typeParamTypesToString(final String signature, final boolean chopit) {
1294
1295 final StringBuilder typeParams = new StringBuilder("<");
1296 int index = 1;
1297
1298 typeParams.append(typeParamTypeToString(signature.substring(index), chopit));
1299 index += unwrap(CONSUMER_CHARS);
1300
1301 while (signature.charAt(index) != '>') {
1302 typeParams.append(", ");
1303 typeParams.append(typeParamTypeToString(signature.substring(index), chopit));
1304 index += unwrap(CONSUMER_CHARS);
1305 }
1306 wrap(CONSUMER_CHARS, index + 1);
1307 return typeParams.append(">").toString();
1308 }
1309
1310
1311
1312
1313
1314
1315
1316
1317 private static String typeParamTypeToString(final String signature, final boolean chopit) {
1318 int index = signature.indexOf(':');
1319 if (index <= 0) {
1320 throw new ClassFormatException("Invalid type parameter signature: " + signature);
1321 }
1322
1323 final StringBuilder typeParam = new StringBuilder(signature.substring(0, index));
1324 index++;
1325 if (signature.charAt(index) != ':') {
1326
1327 typeParam.append(" extends ");
1328 typeParam.append(typeSignatureToString(signature.substring(index), chopit));
1329 index += unwrap(CONSUMER_CHARS);
1330 }
1331
1332 while (signature.charAt(index) == ':') {
1333 index++;
1334 typeParam.append(" & ");
1335 typeParam.append(typeSignatureToString(signature.substring(index), chopit));
1336 index += unwrap(CONSUMER_CHARS);
1337 }
1338 wrap(CONSUMER_CHARS, index);
1339 return typeParam.toString();
1340 }
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350 private static String typeSignaturesToString(final String signature, final boolean chopit, final char term) {
1351
1352 final StringBuilder typeList = new StringBuilder(signature.substring(0, 1));
1353 int index = 1;
1354
1355 if (signature.charAt(index) != term) {
1356 typeList.append(typeSignatureToString(signature.substring(index), chopit));
1357 index += unwrap(CONSUMER_CHARS);
1358 }
1359
1360 while (signature.charAt(index) != term) {
1361 typeList.append(", ");
1362 typeList.append(typeSignatureToString(signature.substring(index), chopit));
1363 index += unwrap(CONSUMER_CHARS);
1364 }
1365 wrap(CONSUMER_CHARS, index + 1);
1366 return typeList.append(term).toString();
1367 }
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380 public static String typeSignatureToString(final String signature, final boolean chopit) throws ClassFormatException {
1381
1382 wrap(CONSUMER_CHARS, 1);
1383 try {
1384 switch (signature.charAt(0)) {
1385 case 'B':
1386 return "byte";
1387 case 'C':
1388 return "char";
1389 case 'D':
1390 return "double";
1391 case 'F':
1392 return "float";
1393 case 'I':
1394 return "int";
1395 case 'J':
1396 return "long";
1397 case 'T': {
1398 final int index = signature.indexOf(';');
1399 if (index < 0) {
1400 throw new ClassFormatException("Invalid type variable signature: " + signature);
1401 }
1402
1403 wrap(CONSUMER_CHARS, index + 1);
1404 return compactClassName(signature.substring(1, index), chopit);
1405 }
1406 case 'L': {
1407
1408
1409 int fromIndex = signature.indexOf('<');
1410 if (fromIndex < 0) {
1411 fromIndex = 0;
1412 } else {
1413 fromIndex = signature.indexOf('>', fromIndex);
1414 if (fromIndex < 0) {
1415 throw new ClassFormatException("Invalid signature: " + signature);
1416 }
1417 }
1418 final int index = signature.indexOf(';', fromIndex);
1419 if (index < 0) {
1420 throw new ClassFormatException("Invalid signature: " + signature);
1421 }
1422
1423
1424 final int bracketIndex = signature.substring(0, index).indexOf('<');
1425 if (bracketIndex < 0) {
1426
1427 wrap(CONSUMER_CHARS, index + 1);
1428 return compactClassName(signature.substring(1, index), chopit);
1429 }
1430
1431 fromIndex = signature.indexOf(';');
1432 if (fromIndex < 0) {
1433 throw new ClassFormatException("Invalid signature: " + signature);
1434 }
1435 if (fromIndex < bracketIndex) {
1436
1437 wrap(CONSUMER_CHARS, fromIndex + 1);
1438 return compactClassName(signature.substring(1, fromIndex), chopit);
1439 }
1440
1441
1442
1443 final StringBuilder type = new StringBuilder(compactClassName(signature.substring(1, bracketIndex), chopit)).append("<");
1444 int consumedChars = bracketIndex + 1;
1445
1446
1447 if (signature.charAt(consumedChars) == '+') {
1448 type.append("? extends ");
1449 consumedChars++;
1450 } else if (signature.charAt(consumedChars) == '-') {
1451 type.append("? super ");
1452 consumedChars++;
1453 }
1454
1455
1456 if (signature.charAt(consumedChars) == '*') {
1457 type.append("?");
1458 consumedChars++;
1459 } else {
1460 type.append(typeSignatureToString(signature.substring(consumedChars), chopit));
1461
1462 consumedChars = unwrap(CONSUMER_CHARS) + consumedChars;
1463 wrap(CONSUMER_CHARS, consumedChars);
1464 }
1465
1466
1467 while (signature.charAt(consumedChars) != '>') {
1468 type.append(", ");
1469
1470 if (signature.charAt(consumedChars) == '+') {
1471 type.append("? extends ");
1472 consumedChars++;
1473 } else if (signature.charAt(consumedChars) == '-') {
1474 type.append("? super ");
1475 consumedChars++;
1476 }
1477 if (signature.charAt(consumedChars) == '*') {
1478 type.append("?");
1479 consumedChars++;
1480 } else {
1481 type.append(typeSignatureToString(signature.substring(consumedChars), chopit));
1482
1483 consumedChars = unwrap(CONSUMER_CHARS) + consumedChars;
1484 wrap(CONSUMER_CHARS, consumedChars);
1485 }
1486 }
1487
1488
1489 consumedChars++;
1490 type.append(">");
1491
1492 if (signature.charAt(consumedChars) == '.') {
1493
1494 type.append(".");
1495
1496
1497 type.append(typeSignatureToString("L" + signature.substring(consumedChars + 1), chopit));
1498
1499
1500
1501 consumedChars = unwrap(CONSUMER_CHARS) + consumedChars;
1502 wrap(CONSUMER_CHARS, consumedChars);
1503 return type.toString();
1504 }
1505 if (signature.charAt(consumedChars) != ';') {
1506 throw new ClassFormatException("Invalid signature: " + signature);
1507 }
1508 wrap(CONSUMER_CHARS, consumedChars + 1);
1509 return type.toString();
1510 }
1511 case 'S':
1512 return "short";
1513 case 'Z':
1514 return "boolean";
1515 case '[': {
1516 int n;
1517 final StringBuilder brackets = new StringBuilder();
1518
1519 for (n = 0; signature.charAt(n) == '['; n++) {
1520 brackets.append("[]");
1521 }
1522 final int consumedChars = n;
1523
1524 final String type = typeSignatureToString(signature.substring(n), chopit);
1525
1526
1527 final int temp = unwrap(CONSUMER_CHARS) + consumedChars;
1528 wrap(CONSUMER_CHARS, temp);
1529 return type + brackets.toString();
1530 }
1531 case 'V':
1532 return "void";
1533 default:
1534 throw new ClassFormatException("Invalid signature: '" + signature + "'");
1535 }
1536 } catch (final StringIndexOutOfBoundsException e) {
1537 throw new ClassFormatException("Invalid signature: " + signature, e);
1538 }
1539 }
1540
1541 private static int unwrap(final ThreadLocal<Integer> tl) {
1542 return tl.get().intValue();
1543 }
1544
1545 private static void wrap(final ThreadLocal<Integer> tl, final int value) {
1546 tl.set(Integer.valueOf(value));
1547 }
1548
1549 }