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