001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.bcel.classfile; 019 020import java.io.ByteArrayInputStream; 021import java.io.ByteArrayOutputStream; 022import java.io.CharArrayReader; 023import java.io.CharArrayWriter; 024import java.io.FilterReader; 025import java.io.FilterWriter; 026import java.io.IOException; 027import java.io.PrintStream; 028import java.io.PrintWriter; 029import java.io.Reader; 030import java.io.Writer; 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.List; 034import java.util.Locale; 035import java.util.zip.GZIPInputStream; 036import java.util.zip.GZIPOutputStream; 037 038import org.apache.bcel.Const; 039import org.apache.bcel.util.ByteSequence; 040import org.apache.commons.lang3.ArrayUtils; 041 042/** 043 * Utility functions that do not really belong to any class in particular. 044 * 045 */ 046// @since 6.0 methods are no longer final 047public abstract class Utility { 048 049 /** 050 * Decode characters into bytes. Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a> 051 */ 052 private static class JavaReader extends FilterReader { 053 054 public JavaReader(final Reader in) { 055 super(in); 056 } 057 058 @Override 059 public int read() throws IOException { 060 final int b = in.read(); 061 if (b != ESCAPE_CHAR) { 062 return b; 063 } 064 final int i = in.read(); 065 if (i < 0) { 066 return -1; 067 } 068 if (i >= '0' && i <= '9' || i >= 'a' && i <= 'f') { // Normal escape 069 final int j = in.read(); 070 if (j < 0) { 071 return -1; 072 } 073 final char[] tmp = {(char) i, (char) j}; 074 return Integer.parseInt(new String(tmp), 16); 075 } 076 return MAP_CHAR[i]; 077 } 078 079 @Override 080 public int read(final char[] cbuf, final int off, final int len) throws IOException { 081 for (int i = 0; i < len; i++) { 082 cbuf[off + i] = (char) read(); 083 } 084 return len; 085 } 086 } 087 088 /** 089 * Encode bytes into valid java identifier characters. Used by 090 * <a href="Utility.html#encode(byte[], boolean)">encode()</a> 091 */ 092 private static class JavaWriter extends FilterWriter { 093 094 public JavaWriter(final Writer out) { 095 super(out); 096 } 097 098 @Override 099 public void write(final char[] cbuf, final int off, final int len) throws IOException { 100 for (int i = 0; i < len; i++) { 101 write(cbuf[off + i]); 102 } 103 } 104 105 @Override 106 public void write(final int b) throws IOException { 107 if (isJavaIdentifierPart((char) b) && b != ESCAPE_CHAR) { 108 out.write(b); 109 } else { 110 out.write(ESCAPE_CHAR); // Escape character 111 // Special escape 112 if (b >= 0 && b < FREE_CHARS) { 113 out.write(CHAR_MAP[b]); 114 } else { // Normal escape 115 final char[] tmp = Integer.toHexString(b).toCharArray(); 116 if (tmp.length == 1) { 117 out.write('0'); 118 out.write(tmp[0]); 119 } else { 120 out.write(tmp[0]); 121 out.write(tmp[1]); 122 } 123 } 124 } 125 } 126 127 @Override 128 public void write(final String str, final int off, final int len) throws IOException { 129 write(str.toCharArray(), off, len); 130 } 131 } 132 133 /* 134 * How many chars have been consumed during parsing in typeSignatureToString(). Read by methodSignatureToString(). Set 135 * by side effect, but only internally. 136 */ 137 private static final ThreadLocal<Integer> CONSUMER_CHARS = ThreadLocal.withInitial(() -> Integer.valueOf(0)); 138 139 /* 140 * The `WIDE' instruction is used in the byte code to allow 16-bit wide indices for local variables. This opcode 141 * precedes an `ILOAD', e.g.. The opcode immediately following takes an extra byte which is combined with the following 142 * byte to form a 16-bit value. 143 */ 144 private static boolean wide; 145 146 // A-Z, g-z, _, $ 147 private static final int FREE_CHARS = 48; 148 149 private static final int[] CHAR_MAP = new int[FREE_CHARS]; 150 151 private static final int[] MAP_CHAR = new int[256]; // Reverse map 152 153 private static final char ESCAPE_CHAR = '$'; 154 155 static { 156 int j = 0; 157 for (int i = 'A'; i <= 'Z'; i++) { 158 CHAR_MAP[j] = i; 159 MAP_CHAR[i] = j; 160 j++; 161 } 162 for (int i = 'g'; i <= 'z'; i++) { 163 CHAR_MAP[j] = i; 164 MAP_CHAR[i] = j; 165 j++; 166 } 167 CHAR_MAP[j] = '$'; 168 MAP_CHAR['$'] = j; 169 j++; 170 CHAR_MAP[j] = '_'; 171 MAP_CHAR['_'] = j; 172 } 173 174 /** 175 * Convert bit field of flags into string such as `static final'. 176 * 177 * @param access_flags Access flags 178 * @return String representation of flags 179 */ 180 public static String accessToString(final int access_flags) { 181 return accessToString(access_flags, false); 182 } 183 184 /** 185 * Convert bit field of flags into string such as `static final'. 186 * 187 * Special case: Classes compiled with new compilers and with the `ACC_SUPER' flag would be said to be "synchronized". 188 * This is because SUN used the same value for the flags `ACC_SUPER' and `ACC_SYNCHRONIZED'. 189 * 190 * @param access_flags Access flags 191 * @param for_class access flags are for class qualifiers ? 192 * @return String representation of flags 193 */ 194 public static String accessToString(final int access_flags, final boolean for_class) { 195 final StringBuilder buf = new StringBuilder(); 196 int p = 0; 197 for (int i = 0; p < Const.MAX_ACC_FLAG_I; i++) { // Loop through known flags 198 p = pow2(i); 199 if ((access_flags & p) != 0) { 200 /* 201 * Special case: Classes compiled with new compilers and with the `ACC_SUPER' flag would be said to be "synchronized". 202 * This is because SUN used the same value for the flags `ACC_SUPER' and `ACC_SYNCHRONIZED'. 203 */ 204 if (for_class && (p == Const.ACC_SUPER || p == Const.ACC_INTERFACE)) { 205 continue; 206 } 207 buf.append(Const.getAccessName(i)).append(" "); 208 } 209 } 210 return buf.toString().trim(); 211 } 212 213 /** 214 * Convert (signed) byte to (unsigned) short value, i.e., all negative values become positive. 215 */ 216 private static short byteToShort(final byte b) { 217 return b < 0 ? (short) (256 + b) : (short) b; 218 } 219 220 /** 221 * @param access_flags the class flags 222 * 223 * @return "class" or "interface", depending on the ACC_INTERFACE flag 224 */ 225 public static String classOrInterface(final int access_flags) { 226 return (access_flags & Const.ACC_INTERFACE) != 0 ? "interface" : "class"; 227 } 228 229 /** 230 * @return `flag' with bit `i' set to 0 231 */ 232 public static int clearBit(final int flag, final int i) { 233 final int bit = pow2(i); 234 return (flag & bit) == 0 ? flag : flag ^ bit; 235 } 236 237 public static String codeToString(final byte[] code, final ConstantPool constant_pool, final int index, final int length) { 238 return codeToString(code, constant_pool, index, length, true); 239 } 240 241 /** 242 * Disassemble a byte array of JVM byte codes starting from code line `index' and return the disassembled string 243 * representation. Decode only `num' opcodes (including their operands), use -1 if you want to decompile everything. 244 * 245 * @param code byte code array 246 * @param constant_pool Array of constants 247 * @param index offset in `code' array <EM>(number of opcodes, not bytes!)</EM> 248 * @param length number of opcodes to decompile, -1 for all 249 * @param verbose be verbose, e.g. print constant pool index 250 * @return String representation of byte codes 251 */ 252 public static String codeToString(final byte[] code, final ConstantPool constant_pool, final int index, final int length, final boolean verbose) { 253 final StringBuilder buf = new StringBuilder(code.length * 20); // Should be sufficient // CHECKSTYLE IGNORE MagicNumber 254 try (ByteSequence stream = new ByteSequence(code)) { 255 for (int i = 0; i < index; i++) { 256 codeToString(stream, constant_pool, verbose); 257 } 258 for (int i = 0; stream.available() > 0; i++) { 259 if (length < 0 || i < length) { 260 final String indices = fillup(stream.getIndex() + ":", 6, true, ' '); 261 buf.append(indices).append(codeToString(stream, constant_pool, verbose)).append('\n'); 262 } 263 } 264 } catch (final IOException e) { 265 throw new ClassFormatException("Byte code error: " + buf.toString(), e); 266 } 267 return buf.toString(); 268 } 269 270 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool) throws IOException { 271 return codeToString(bytes, constant_pool, true); 272 } 273 274 /** 275 * Disassemble a stream of byte codes and return the string representation. 276 * 277 * @param bytes stream of bytes 278 * @param constant_pool Array of constants 279 * @param verbose be verbose, e.g. print constant pool index 280 * @return String representation of byte code 281 * 282 * @throws IOException if a failure from reading from the bytes argument occurs 283 */ 284 public static String codeToString(final ByteSequence bytes, final ConstantPool constant_pool, final boolean verbose) throws IOException { 285 final short opcode = (short) bytes.readUnsignedByte(); 286 int default_offset = 0; 287 int low; 288 int high; 289 int npairs; 290 int index; 291 int vindex; 292 int constant; 293 int[] match; 294 int[] jump_table; 295 int no_pad_bytes = 0; 296 int offset; 297 final StringBuilder buf = new StringBuilder(Const.getOpcodeName(opcode)); 298 /* 299 * Special case: Skip (0-3) padding bytes, i.e., the following bytes are 4-byte-aligned 300 */ 301 if (opcode == Const.TABLESWITCH || opcode == Const.LOOKUPSWITCH) { 302 final int remainder = bytes.getIndex() % 4; 303 no_pad_bytes = remainder == 0 ? 0 : 4 - remainder; 304 for (int i = 0; i < no_pad_bytes; i++) { 305 byte b; 306 if ((b = bytes.readByte()) != 0) { 307 System.err.println("Warning: Padding byte != 0 in " + Const.getOpcodeName(opcode) + ":" + b); 308 } 309 } 310 // Both cases have a field default_offset in common 311 default_offset = bytes.readInt(); 312 } 313 switch (opcode) { 314 /* 315 * Table switch has variable length arguments. 316 */ 317 case Const.TABLESWITCH: 318 low = bytes.readInt(); 319 high = bytes.readInt(); 320 offset = bytes.getIndex() - 12 - no_pad_bytes - 1; 321 default_offset += offset; 322 buf.append("\tdefault = ").append(default_offset).append(", low = ").append(low).append(", high = ").append(high).append("("); 323 jump_table = new int[high - low + 1]; 324 for (int i = 0; i < jump_table.length; i++) { 325 jump_table[i] = offset + bytes.readInt(); 326 buf.append(jump_table[i]); 327 if (i < jump_table.length - 1) { 328 buf.append(", "); 329 } 330 } 331 buf.append(")"); 332 break; 333 /* 334 * Lookup switch has variable length arguments. 335 */ 336 case Const.LOOKUPSWITCH: { 337 npairs = bytes.readInt(); 338 offset = bytes.getIndex() - 8 - no_pad_bytes - 1; 339 match = new int[npairs]; 340 jump_table = new int[npairs]; 341 default_offset += offset; 342 buf.append("\tdefault = ").append(default_offset).append(", npairs = ").append(npairs).append(" ("); 343 for (int i = 0; i < npairs; i++) { 344 match[i] = bytes.readInt(); 345 jump_table[i] = offset + bytes.readInt(); 346 buf.append("(").append(match[i]).append(", ").append(jump_table[i]).append(")"); 347 if (i < npairs - 1) { 348 buf.append(", "); 349 } 350 } 351 buf.append(")"); 352 } 353 break; 354 /* 355 * Two address bytes + offset from start of byte stream form the jump target 356 */ 357 case Const.GOTO: 358 case Const.IFEQ: 359 case Const.IFGE: 360 case Const.IFGT: 361 case Const.IFLE: 362 case Const.IFLT: 363 case Const.JSR: 364 case Const.IFNE: 365 case Const.IFNONNULL: 366 case Const.IFNULL: 367 case Const.IF_ACMPEQ: 368 case Const.IF_ACMPNE: 369 case Const.IF_ICMPEQ: 370 case Const.IF_ICMPGE: 371 case Const.IF_ICMPGT: 372 case Const.IF_ICMPLE: 373 case Const.IF_ICMPLT: 374 case Const.IF_ICMPNE: 375 buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readShort()); 376 break; 377 /* 378 * 32-bit wide jumps 379 */ 380 case Const.GOTO_W: 381 case Const.JSR_W: 382 buf.append("\t\t#").append(bytes.getIndex() - 1 + bytes.readInt()); 383 break; 384 /* 385 * Index byte references local variable (register) 386 */ 387 case Const.ALOAD: 388 case Const.ASTORE: 389 case Const.DLOAD: 390 case Const.DSTORE: 391 case Const.FLOAD: 392 case Const.FSTORE: 393 case Const.ILOAD: 394 case Const.ISTORE: 395 case Const.LLOAD: 396 case Const.LSTORE: 397 case Const.RET: 398 if (wide) { 399 vindex = bytes.readUnsignedShort(); 400 wide = false; // Clear flag 401 } else { 402 vindex = bytes.readUnsignedByte(); 403 } 404 buf.append("\t\t%").append(vindex); 405 break; 406 /* 407 * Remember wide byte which is used to form a 16-bit address in the following instruction. Relies on that the method is 408 * called again with the following opcode. 409 */ 410 case Const.WIDE: 411 wide = true; 412 buf.append("\t(wide)"); 413 break; 414 /* 415 * Array of basic type. 416 */ 417 case Const.NEWARRAY: 418 buf.append("\t\t<").append(Const.getTypeName(bytes.readByte())).append(">"); 419 break; 420 /* 421 * Access object/class fields. 422 */ 423 case Const.GETFIELD: 424 case Const.GETSTATIC: 425 case Const.PUTFIELD: 426 case Const.PUTSTATIC: 427 index = bytes.readUnsignedShort(); 428 buf.append("\t\t").append(constant_pool.constantToString(index, Const.CONSTANT_Fieldref)).append(verbose ? " (" + index + ")" : ""); 429 break; 430 /* 431 * Operands are references to classes in constant pool 432 */ 433 case Const.NEW: 434 case Const.CHECKCAST: 435 buf.append("\t"); 436 //$FALL-THROUGH$ 437 case Const.INSTANCEOF: 438 index = bytes.readUnsignedShort(); 439 buf.append("\t<").append(constant_pool.constantToString(index, Const.CONSTANT_Class)).append(">").append(verbose ? " (" + index + ")" : ""); 440 break; 441 /* 442 * Operands are references to methods in constant pool 443 */ 444 case Const.INVOKESPECIAL: 445 case Const.INVOKESTATIC: 446 index = bytes.readUnsignedShort(); 447 final Constant c = constant_pool.getConstant(index); 448 // With Java8 operand may be either a CONSTANT_Methodref 449 // or a CONSTANT_InterfaceMethodref. (markro) 450 buf.append("\t").append(constant_pool.constantToString(index, c.getTag())).append(verbose ? " (" + index + ")" : ""); 451 break; 452 case Const.INVOKEVIRTUAL: 453 index = bytes.readUnsignedShort(); 454 buf.append("\t").append(constant_pool.constantToString(index, Const.CONSTANT_Methodref)).append(verbose ? " (" + index + ")" : ""); 455 break; 456 case Const.INVOKEINTERFACE: 457 index = bytes.readUnsignedShort(); 458 final int nargs = bytes.readUnsignedByte(); // historical, redundant 459 buf.append("\t").append(constant_pool.constantToString(index, Const.CONSTANT_InterfaceMethodref)).append(verbose ? " (" + index + ")\t" : "") 460 .append(nargs).append("\t").append(bytes.readUnsignedByte()); // Last byte is a reserved space 461 break; 462 case Const.INVOKEDYNAMIC: 463 index = bytes.readUnsignedShort(); 464 buf.append("\t").append(constant_pool.constantToString(index, Const.CONSTANT_InvokeDynamic)).append(verbose ? " (" + index + ")\t" : "") 465 .append(bytes.readUnsignedByte()) // Thrid byte is a reserved space 466 .append(bytes.readUnsignedByte()); // Last byte is a reserved space 467 break; 468 /* 469 * Operands are references to items in constant pool 470 */ 471 case Const.LDC_W: 472 case Const.LDC2_W: 473 index = bytes.readUnsignedShort(); 474 buf.append("\t\t").append(constant_pool.constantToString(index, constant_pool.getConstant(index).getTag())) 475 .append(verbose ? " (" + index + ")" : ""); 476 break; 477 case Const.LDC: 478 index = bytes.readUnsignedByte(); 479 buf.append("\t\t").append(constant_pool.constantToString(index, constant_pool.getConstant(index).getTag())) 480 .append(verbose ? " (" + index + ")" : ""); 481 break; 482 /* 483 * Array of references. 484 */ 485 case Const.ANEWARRAY: 486 index = bytes.readUnsignedShort(); 487 buf.append("\t\t<").append(compactClassName(constant_pool.getConstantString(index, Const.CONSTANT_Class), false)).append(">") 488 .append(verbose ? " (" + index + ")" : ""); 489 break; 490 /* 491 * Multidimensional array of references. 492 */ 493 case Const.MULTIANEWARRAY: { 494 index = bytes.readUnsignedShort(); 495 final int dimensions = bytes.readUnsignedByte(); 496 buf.append("\t<").append(compactClassName(constant_pool.getConstantString(index, Const.CONSTANT_Class), false)).append(">\t").append(dimensions) 497 .append(verbose ? " (" + index + ")" : ""); 498 } 499 break; 500 /* 501 * Increment local variable. 502 */ 503 case Const.IINC: 504 if (wide) { 505 vindex = bytes.readUnsignedShort(); 506 constant = bytes.readShort(); 507 wide = false; 508 } else { 509 vindex = bytes.readUnsignedByte(); 510 constant = bytes.readByte(); 511 } 512 buf.append("\t\t%").append(vindex).append("\t").append(constant); 513 break; 514 default: 515 if (Const.getNoOfOperands(opcode) > 0) { 516 for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) { 517 buf.append("\t\t"); 518 switch (Const.getOperandType(opcode, i)) { 519 case Const.T_BYTE: 520 buf.append(bytes.readByte()); 521 break; 522 case Const.T_SHORT: 523 buf.append(bytes.readShort()); 524 break; 525 case Const.T_INT: 526 buf.append(bytes.readInt()); 527 break; 528 default: // Never reached 529 throw new IllegalStateException("Unreachable default case reached!"); 530 } 531 } 532 } 533 } 534 return buf.toString(); 535 } 536 537 /** 538 * Shorten long class names, <em>java/lang/String</em> becomes <em>String</em>. 539 * 540 * @param str The long class name 541 * @return Compacted class name 542 */ 543 public static String compactClassName(final String str) { 544 return compactClassName(str, true); 545 } 546 547 /** 548 * Shorten long class names, <em>java/lang/String</em> becomes <em>java.lang.String</em>, e.g.. If <em>chopit</em> is 549 * <em>true</em> the prefix <em>java.lang</em> is also removed. 550 * 551 * @param str The long class name 552 * @param chopit flag that determines whether chopping is executed or not 553 * @return Compacted class name 554 */ 555 public static String compactClassName(final String str, final boolean chopit) { 556 return compactClassName(str, "java.lang.", chopit); 557 } 558 559 /** 560 * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>, if the class name starts with this string 561 * and the flag <em>chopit</em> is true. Slashes <em>/</em> are converted to dots <em>.</em>. 562 * 563 * @param str The long class name 564 * @param prefix The prefix the get rid off 565 * @param chopit flag that determines whether chopping is executed or not 566 * @return Compacted class name 567 */ 568 public static String compactClassName(String str, final String prefix, final boolean chopit) { 569 final int len = prefix.length(); 570 str = pathToPackage(str); // Is `/' on all systems, even DOS 571 // If string starts with `prefix' and contains no further dots 572 if (chopit && str.startsWith(prefix) && str.substring(len).indexOf('.') == -1) { 573 str = str.substring(len); 574 } 575 return str; 576 } 577 578 /** 579 * Converts a path to a package name. 580 * 581 * @param str the source path. 582 * @return a package name. 583 * @since 6.6.0 584 */ 585 public static String pathToPackage(final String str) { 586 return str.replace('/', '.'); 587 } 588 589 /** 590 * Escape all occurences of newline chars '\n', quotes \", etc. 591 */ 592 public static String convertString(final String label) { 593 final char[] ch = label.toCharArray(); 594 final StringBuilder buf = new StringBuilder(); 595 for (final char element : ch) { 596 switch (element) { 597 case '\n': 598 buf.append("\\n"); 599 break; 600 case '\r': 601 buf.append("\\r"); 602 break; 603 case '\"': 604 buf.append("\\\""); 605 break; 606 case '\'': 607 buf.append("\\'"); 608 break; 609 case '\\': 610 buf.append("\\\\"); 611 break; 612 default: 613 buf.append(element); 614 break; 615 } 616 } 617 return buf.toString(); 618 } 619 620 private static int countBrackets(final String brackets) { 621 final char[] chars = brackets.toCharArray(); 622 int count = 0; 623 boolean open = false; 624 for (final char c : chars) { 625 switch (c) { 626 case '[': 627 if (open) { 628 throw new IllegalArgumentException("Illegally nested brackets:" + brackets); 629 } 630 open = true; 631 break; 632 case ']': 633 if (!open) { 634 throw new IllegalArgumentException("Illegally nested brackets:" + brackets); 635 } 636 open = false; 637 count++; 638 break; 639 default: 640 // Don't care 641 break; 642 } 643 } 644 if (open) { 645 throw new IllegalArgumentException("Illegally nested brackets:" + brackets); 646 } 647 return count; 648 } 649 650 /** 651 * Decode a string back to a byte array. 652 * 653 * @param s the string to convert 654 * @param uncompress use gzip to uncompress the stream of bytes 655 * 656 * @throws IOException if there's a gzip exception 657 */ 658 public static byte[] decode(final String s, final boolean uncompress) throws IOException { 659 byte[] bytes; 660 try (JavaReader jr = new JavaReader(new CharArrayReader(s.toCharArray())); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { 661 int ch; 662 while ((ch = jr.read()) >= 0) { 663 bos.write(ch); 664 } 665 bytes = bos.toByteArray(); 666 } 667 if (uncompress) { 668 final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes)); 669 final byte[] tmp = new byte[bytes.length * 3]; // Rough estimate 670 int count = 0; 671 int b; 672 while ((b = gis.read()) >= 0) { 673 tmp[count++] = (byte) b; 674 } 675 bytes = new byte[count]; 676 System.arraycopy(tmp, 0, bytes, 0, count); 677 } 678 return bytes; 679 } 680 681 /** 682 * Encode byte array it into Java identifier string, i.e., a string that only contains the following characters: (a, ... 683 * z, A, ... Z, 0, ... 9, _, $). The encoding algorithm itself is not too clever: if the current byte's ASCII value 684 * already is a valid Java identifier part, leave it as it is. Otherwise it writes the escape character($) followed by: 685 * 686 * <ul> 687 * <li>the ASCII value as a hexadecimal string, if the value is not in the range 200..247</li> 688 * <li>a Java identifier char not used in a lowercase hexadecimal string, if the value is in the range 200..247</li> 689 * </ul> 690 * 691 * <p> 692 * This operation inflates the original byte array by roughly 40-50% 693 * </p> 694 * 695 * @param bytes the byte array to convert 696 * @param compress use gzip to minimize string 697 * 698 * @throws IOException if there's a gzip exception 699 */ 700 public static String encode(byte[] bytes, final boolean compress) throws IOException { 701 if (compress) { 702 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos)) { 703 gos.write(bytes, 0, bytes.length); 704 gos.close(); 705 bytes = baos.toByteArray(); 706 } 707 } 708 final CharArrayWriter caw = new CharArrayWriter(); 709 try (JavaWriter jw = new JavaWriter(caw)) { 710 for (final byte b : bytes) { 711 final int in = b & 0x000000ff; // Normalize to unsigned 712 jw.write(in); 713 } 714 } 715 return caw.toString(); 716 } 717 718 static boolean equals(final byte[] a, final byte[] b) { 719 int size; 720 if ((size = a.length) != b.length) { 721 return false; 722 } 723 for (int i = 0; i < size; i++) { 724 if (a[i] != b[i]) { 725 return false; 726 } 727 } 728 return true; 729 } 730 731 /** 732 * Fillup char with up to length characters with char `fill' and justify it left or right. 733 * 734 * @param str string to format 735 * @param length length of desired string 736 * @param left_justify format left or right 737 * @param fill fill character 738 * @return formatted string 739 */ 740 public static String fillup(final String str, final int length, final boolean left_justify, final char fill) { 741 final int len = length - str.length(); 742 final char[] buf = new char[Math.max(len, 0)]; 743 Arrays.fill(buf, fill); 744 if (left_justify) { 745 return str + new String(buf); 746 } 747 return new String(buf) + str; 748 } 749 750 /** 751 * WARNING: 752 * 753 * There is some nomenclature confusion through much of the BCEL code base with respect to the terms Descriptor and 754 * Signature. For the offical definitions see: 755 * 756 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.3"> Descriptors in The Java 757 * Virtual Machine Specification</a> 758 * 759 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1"> Signatures in The Java 760 * Virtual Machine Specification</a> 761 * 762 * In brief, a descriptor is a string representing the type of a field or method. Signatures are similar, but more 763 * complex. Signatures are used to encode declarations written in the Java programming language that use types 764 * outside the type system of the Java Virtual Machine. They are used to describe the type of any class, interface, 765 * constructor, method or field whose declaration uses type variables or parameterized types. 766 * 767 * To parse a descriptor, call typeSignatureToString. To parse a signature, call signatureToString. 768 * 769 * Note that if the signature string is a single, non-generic item, the call to signatureToString reduces to a call 770 * to typeSignatureToString. Also note, that if you only wish to parse the first item in a longer signature string, 771 * you should call typeSignatureToString directly. 772 */ 773 774 /** 775 * Return a string for an integer justified left or right and filled up with `fill' characters if necessary. 776 * 777 * @param i integer to format 778 * @param length length of desired string 779 * @param left_justify format left or right 780 * @param fill fill character 781 * @return formatted int 782 */ 783 public static String format(final int i, final int length, final boolean left_justify, final char fill) { 784 return fillup(Integer.toString(i), length, left_justify, fill); 785 } 786 787 /** 788 * Parse Java type such as "char", or "java.lang.String[]" and return the signature in byte code format, e.g. "C" or 789 * "[Ljava/lang/String;" respectively. 790 * 791 * @param type Java type 792 * @return byte code signature 793 */ 794 public static String getSignature(String type) { 795 final StringBuilder buf = new StringBuilder(); 796 final char[] chars = type.toCharArray(); 797 boolean char_found = false; 798 boolean delim = false; 799 int index = -1; 800 loop: for (int i = 0; i < chars.length; i++) { 801 switch (chars[i]) { 802 case ' ': 803 case '\t': 804 case '\n': 805 case '\r': 806 case '\f': 807 if (char_found) { 808 delim = true; 809 } 810 break; 811 case '[': 812 if (!char_found) { 813 throw new IllegalArgumentException("Illegal type: " + type); 814 } 815 index = i; 816 break loop; 817 default: 818 char_found = true; 819 if (!delim) { 820 buf.append(chars[i]); 821 } 822 } 823 } 824 int brackets = 0; 825 if (index > 0) { 826 brackets = countBrackets(type.substring(index)); 827 } 828 type = buf.toString(); 829 buf.setLength(0); 830 for (int i = 0; i < brackets; i++) { 831 buf.append('['); 832 } 833 boolean found = false; 834 for (int i = Const.T_BOOLEAN; i <= Const.T_VOID && !found; i++) { 835 if (Const.getTypeName(i).equals(type)) { 836 found = true; 837 buf.append(Const.getShortTypeName(i)); 838 } 839 } 840 if (!found) { 841 buf.append('L').append(type.replace('.', '/')).append(';'); 842 } 843 return buf.toString(); 844 } 845 846 /** 847 * @param ch the character to test if it's part of an identifier 848 * 849 * @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _) 850 */ 851 public static boolean isJavaIdentifierPart(final char ch) { 852 return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == '_'; 853 } 854 855 /** 856 * @return true, if bit `i' in `flag' is set 857 */ 858 public static boolean isSet(final int flag, final int i) { 859 return (flag & pow2(i)) != 0; 860 } 861 862 /** 863 * Converts argument list portion of method signature to string with all class names compacted. 864 * 865 * @param signature Method signature 866 * @return String Array of argument types 867 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 868 */ 869 public static String[] methodSignatureArgumentTypes(final String signature) throws ClassFormatException { 870 return methodSignatureArgumentTypes(signature, true); 871 } 872 873 /** 874 * Converts argument list portion of method signature to string. 875 * 876 * @param signature Method signature 877 * @param chopit flag that determines whether chopping is executed or not 878 * @return String Array of argument types 879 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 880 */ 881 public static String[] methodSignatureArgumentTypes(final String signature, final boolean chopit) throws ClassFormatException { 882 final List<String> vec = new ArrayList<>(); 883 int index; 884 try { 885 // Skip any type arguments to read argument declarations between `(' and `)' 886 index = signature.indexOf('(') + 1; 887 if (index <= 0) { 888 throw new ClassFormatException("Invalid method signature: " + signature); 889 } 890 while (signature.charAt(index) != ')') { 891 vec.add(typeSignatureToString(signature.substring(index), chopit)); 892 // corrected concurrent private static field acess 893 index += unwrap(CONSUMER_CHARS); // update position 894 } 895 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 896 throw new ClassFormatException("Invalid method signature: " + signature, e); 897 } 898 return vec.toArray(ArrayUtils.EMPTY_STRING_ARRAY); 899 } 900 901 /** 902 * Converts return type portion of method signature to string with all class names compacted. 903 * 904 * @param signature Method signature 905 * @return String representation of method return type 906 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 907 */ 908 public static String methodSignatureReturnType(final String signature) throws ClassFormatException { 909 return methodSignatureReturnType(signature, true); 910 } 911 912 /** 913 * Converts return type portion of method signature to string. 914 * 915 * @param signature Method signature 916 * @param chopit flag that determines whether chopping is executed or not 917 * @return String representation of method return type 918 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 919 */ 920 public static String methodSignatureReturnType(final String signature, final boolean chopit) throws ClassFormatException { 921 int index; 922 String type; 923 try { 924 // Read return type after `)' 925 index = signature.lastIndexOf(')') + 1; 926 if (index <= 0) { 927 throw new ClassFormatException("Invalid method signature: " + signature); 928 } 929 type = typeSignatureToString(signature.substring(index), chopit); 930 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 931 throw new ClassFormatException("Invalid method signature: " + signature, e); 932 } 933 return type; 934 } 935 936 /** 937 * Converts method signature to string with all class names compacted. 938 * 939 * @param signature to convert 940 * @param name of method 941 * @param access flags of method 942 * @return Human readable signature 943 */ 944 public static String methodSignatureToString(final String signature, final String name, final String access) { 945 return methodSignatureToString(signature, name, access, true); 946 } 947 948 /** 949 * Converts method signature to string. 950 * 951 * @param signature to convert 952 * @param name of method 953 * @param access flags of method 954 * @param chopit flag that determines whether chopping is executed or not 955 * @return Human readable signature 956 */ 957 public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit) { 958 return methodSignatureToString(signature, name, access, chopit, null); 959 } 960 961 /** 962 * This method converts a method signature string into a Java type declaration like `void main(String[])' and throws a 963 * `ClassFormatException' when the parsed type is invalid. 964 * 965 * @param signature Method signature 966 * @param name Method name 967 * @param access Method access rights 968 * @param chopit flag that determines whether chopping is executed or not 969 * @param vars the LocalVariableTable for the method 970 * @return Java type declaration 971 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 972 */ 973 public static String methodSignatureToString(final String signature, final String name, final String access, final boolean chopit, 974 final LocalVariableTable vars) throws ClassFormatException { 975 final StringBuilder buf = new StringBuilder("("); 976 String type; 977 int index; 978 int var_index = access.contains("static") ? 0 : 1; 979 try { 980 // Skip any type arguments to read argument declarations between `(' and `)' 981 index = signature.indexOf('(') + 1; 982 if (index <= 0) { 983 throw new ClassFormatException("Invalid method signature: " + signature); 984 } 985 while (signature.charAt(index) != ')') { 986 final String paramType = typeSignatureToString(signature.substring(index), chopit); 987 buf.append(paramType); 988 if (vars != null) { 989 final LocalVariable l = vars.getLocalVariable(var_index, 0); 990 if (l != null) { 991 buf.append(" ").append(l.getName()); 992 } 993 } else { 994 buf.append(" arg").append(var_index); 995 } 996 if ("double".equals(paramType) || "long".equals(paramType)) { 997 var_index += 2; 998 } else { 999 var_index++; 1000 } 1001 buf.append(", "); 1002 // corrected concurrent private static field acess 1003 index += unwrap(CONSUMER_CHARS); // update position 1004 } 1005 index++; // update position 1006 // Read return type after `)' 1007 type = typeSignatureToString(signature.substring(index), chopit); 1008 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 1009 throw new ClassFormatException("Invalid method signature: " + signature, e); 1010 } 1011 // ignore any throws information in the signature 1012 if (buf.length() > 1) { 1013 buf.setLength(buf.length() - 2); 1014 } 1015 buf.append(")"); 1016 return access + (!access.isEmpty() ? " " : "") + // May be an empty string 1017 type + " " + name + buf.toString(); 1018 } 1019 1020 /** 1021 * Converts string containing the method return and argument types to a byte code method signature. 1022 * 1023 * @param ret Return type of method 1024 * @param argv Types of method arguments 1025 * @return Byte code representation of method signature 1026 * 1027 * @throws ClassFormatException if the signature is for Void 1028 */ 1029 public static String methodTypeToSignature(final String ret, final String[] argv) throws ClassFormatException { 1030 final StringBuilder buf = new StringBuilder("("); 1031 String str; 1032 if (argv != null) { 1033 for (final String element : argv) { 1034 str = getSignature(element); 1035 if (str.endsWith("V")) { 1036 throw new ClassFormatException("Invalid type: " + element); 1037 } 1038 buf.append(str); 1039 } 1040 } 1041 str = getSignature(ret); 1042 buf.append(")").append(str); 1043 return buf.toString(); 1044 } 1045 1046 private static int pow2(final int n) { 1047 return 1 << n; 1048 } 1049 1050 public static String printArray(final Object[] obj) { 1051 return printArray(obj, true); 1052 } 1053 1054 public static String printArray(final Object[] obj, final boolean braces) { 1055 return printArray(obj, braces, false); 1056 } 1057 1058 public static String printArray(final Object[] obj, final boolean braces, final boolean quote) { 1059 if (obj == null) { 1060 return null; 1061 } 1062 final StringBuilder buf = new StringBuilder(); 1063 if (braces) { 1064 buf.append('{'); 1065 } 1066 for (int i = 0; i < obj.length; i++) { 1067 if (obj[i] != null) { 1068 buf.append(quote ? "\"" : "").append(obj[i]).append(quote ? "\"" : ""); 1069 } else { 1070 buf.append("null"); 1071 } 1072 if (i < obj.length - 1) { 1073 buf.append(", "); 1074 } 1075 } 1076 if (braces) { 1077 buf.append('}'); 1078 } 1079 return buf.toString(); 1080 } 1081 1082 public static void printArray(final PrintStream out, final Object[] obj) { 1083 out.println(printArray(obj, true)); 1084 } 1085 1086 public static void printArray(final PrintWriter out, final Object[] obj) { 1087 out.println(printArray(obj, true)); 1088 } 1089 1090 /** 1091 * Replace all occurrences of <em>old</em> in <em>str</em> with <em>new</em>. 1092 * 1093 * @param str String to permute 1094 * @param old String to be replaced 1095 * @param new_ Replacement string 1096 * @return new String object 1097 */ 1098 public static String replace(String str, final String old, final String new_) { 1099 int index; 1100 int old_index; 1101 try { 1102 if (str.contains(old)) { // `old' found in str 1103 final StringBuilder buf = new StringBuilder(); 1104 old_index = 0; // String start offset 1105 // While we have something to replace 1106 while ((index = str.indexOf(old, old_index)) != -1) { 1107 buf.append(str, old_index, index); // append prefix 1108 buf.append(new_); // append replacement 1109 old_index = index + old.length(); // Skip `old'.length chars 1110 } 1111 buf.append(str.substring(old_index)); // append rest of string 1112 str = buf.toString(); 1113 } 1114 } catch (final StringIndexOutOfBoundsException e) { // Should not occur 1115 System.err.println(e); 1116 } 1117 return str; 1118 } 1119 1120 /** 1121 * Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload" 1122 */ 1123 public static short searchOpcode(String name) { 1124 name = name.toLowerCase(Locale.ENGLISH); 1125 for (short i = 0; i < Const.OPCODE_NAMES_LENGTH; i++) { 1126 if (Const.getOpcodeName(i).equals(name)) { 1127 return i; 1128 } 1129 } 1130 return -1; 1131 } 1132 1133 /** 1134 * @return `flag' with bit `i' set to 1 1135 */ 1136 public static int setBit(final int flag, final int i) { 1137 return flag | pow2(i); 1138 } 1139 1140 /** 1141 * Converts a signature to a string with all class names compacted. Class, Method and Type signatures are supported. 1142 * Enum and Interface signatures are not supported. 1143 * 1144 * @param signature signature to convert 1145 * @return String containg human readable signature 1146 */ 1147 public static String signatureToString(final String signature) { 1148 return signatureToString(signature, true); 1149 } 1150 1151 /** 1152 * Converts a signature to a string. Class, Method and Type signatures are supported. Enum and Interface signatures are 1153 * not supported. 1154 * 1155 * @param signature signature to convert 1156 * @param chopit flag that determines whether chopping is executed or not 1157 * @return String containg human readable signature 1158 */ 1159 public static String signatureToString(final String signature, final boolean chopit) { 1160 String type = ""; 1161 String typeParams = ""; 1162 int index = 0; 1163 if (signature.charAt(0) == '<') { 1164 // we have type paramters 1165 typeParams = typeParamTypesToString(signature, chopit); 1166 index += unwrap(CONSUMER_CHARS); // update position 1167 } 1168 if (signature.charAt(index) == '(') { 1169 // We have a Method signature. 1170 // add types of arguments 1171 type = typeParams + typeSignaturesToString(signature.substring(index), chopit, ')'); 1172 index += unwrap(CONSUMER_CHARS); // update position 1173 // add return type 1174 type = type + typeSignatureToString(signature.substring(index), chopit); 1175 index += unwrap(CONSUMER_CHARS); // update position 1176 // ignore any throws information in the signature 1177 return type; 1178 } 1179 // Could be Class or Type... 1180 type = typeSignatureToString(signature.substring(index), chopit); 1181 index += unwrap(CONSUMER_CHARS); // update position 1182 if (typeParams.isEmpty() && index == signature.length()) { 1183 // We have a Type signature. 1184 return type; 1185 } 1186 // We have a Class signature. 1187 final StringBuilder typeClass = new StringBuilder(typeParams); 1188 typeClass.append(" extends "); 1189 typeClass.append(type); 1190 if (index < signature.length()) { 1191 typeClass.append(" implements "); 1192 typeClass.append(typeSignatureToString(signature.substring(index), chopit)); 1193 index += unwrap(CONSUMER_CHARS); // update position 1194 } 1195 while (index < signature.length()) { 1196 typeClass.append(", "); 1197 typeClass.append(typeSignatureToString(signature.substring(index), chopit)); 1198 index += unwrap(CONSUMER_CHARS); // update position 1199 } 1200 return typeClass.toString(); 1201 } 1202 1203 /** 1204 * Convert bytes into hexadecimal string 1205 * 1206 * @param bytes an array of bytes to convert to hexadecimal 1207 * 1208 * @return bytes as hexadecimal string, e.g. 00 fa 12 ... 1209 */ 1210 public static String toHexString(final byte[] bytes) { 1211 final StringBuilder buf = new StringBuilder(); 1212 for (int i = 0; i < bytes.length; i++) { 1213 final short b = byteToShort(bytes[i]); 1214 final String hex = Integer.toHexString(b); 1215 if (b < 0x10) { 1216 buf.append('0'); 1217 } 1218 buf.append(hex); 1219 if (i < bytes.length - 1) { 1220 buf.append(' '); 1221 } 1222 } 1223 return buf.toString(); 1224 } 1225 1226 /** 1227 * Return type of method signature as a byte value as defined in <em>Constants</em> 1228 * 1229 * @param signature in format described above 1230 * @return type of method signature 1231 * @see Const 1232 * 1233 * @throws ClassFormatException if signature is not a method signature 1234 */ 1235 public static byte typeOfMethodSignature(final String signature) throws ClassFormatException { 1236 int index; 1237 try { 1238 if (signature.charAt(0) != '(') { 1239 throw new ClassFormatException("Invalid method signature: " + signature); 1240 } 1241 index = signature.lastIndexOf(')') + 1; 1242 return typeOfSignature(signature.substring(index)); 1243 } catch (final StringIndexOutOfBoundsException e) { 1244 throw new ClassFormatException("Invalid method signature: " + signature, e); 1245 } 1246 } 1247 1248 /** 1249 * Return type of signature as a byte value as defined in <em>Constants</em> 1250 * 1251 * @param signature in format described above 1252 * @return type of signature 1253 * @see Const 1254 * 1255 * @throws ClassFormatException if signature isn't a known type 1256 */ 1257 public static byte typeOfSignature(final String signature) throws ClassFormatException { 1258 try { 1259 switch (signature.charAt(0)) { 1260 case 'B': 1261 return Const.T_BYTE; 1262 case 'C': 1263 return Const.T_CHAR; 1264 case 'D': 1265 return Const.T_DOUBLE; 1266 case 'F': 1267 return Const.T_FLOAT; 1268 case 'I': 1269 return Const.T_INT; 1270 case 'J': 1271 return Const.T_LONG; 1272 case 'L': 1273 case 'T': 1274 return Const.T_REFERENCE; 1275 case '[': 1276 return Const.T_ARRAY; 1277 case 'V': 1278 return Const.T_VOID; 1279 case 'Z': 1280 return Const.T_BOOLEAN; 1281 case 'S': 1282 return Const.T_SHORT; 1283 case '!': 1284 case '+': 1285 case '*': 1286 return typeOfSignature(signature.substring(1)); 1287 default: 1288 throw new ClassFormatException("Invalid method signature: " + signature); 1289 } 1290 } catch (final StringIndexOutOfBoundsException e) { 1291 throw new ClassFormatException("Invalid method signature: " + signature, e); 1292 } 1293 } 1294 1295 /** 1296 * Converts a type parameter list signature to a string. 1297 * 1298 * @param signature signature to convert 1299 * @param chopit flag that determines whether chopping is executed or not 1300 * @return String containg human readable signature 1301 */ 1302 private static String typeParamTypesToString(final String signature, final boolean chopit) { 1303 // The first character is guranteed to be '<' 1304 final StringBuilder typeParams = new StringBuilder("<"); 1305 int index = 1; // skip the '<' 1306 // get the first TypeParameter 1307 typeParams.append(typeParamTypeToString(signature.substring(index), chopit)); 1308 index += unwrap(CONSUMER_CHARS); // update position 1309 // are there more TypeParameters? 1310 while (signature.charAt(index) != '>') { 1311 typeParams.append(", "); 1312 typeParams.append(typeParamTypeToString(signature.substring(index), chopit)); 1313 index += unwrap(CONSUMER_CHARS); // update position 1314 } 1315 wrap(CONSUMER_CHARS, index + 1); // account for the '>' char 1316 return typeParams.append(">").toString(); 1317 } 1318 1319 /** 1320 * Converts a type parameter signature to a string. 1321 * 1322 * @param signature signature to convert 1323 * @param chopit flag that determines whether chopping is executed or not 1324 * @return String containg human readable signature 1325 */ 1326 private static String typeParamTypeToString(final String signature, final boolean chopit) { 1327 int index = signature.indexOf(':'); 1328 if (index <= 0) { 1329 throw new ClassFormatException("Invalid type parameter signature: " + signature); 1330 } 1331 // get the TypeParameter identifier 1332 final StringBuilder typeParam = new StringBuilder(signature.substring(0, index)); 1333 index++; // account for the ':' 1334 if (signature.charAt(index) != ':') { 1335 // we have a class bound 1336 typeParam.append(" extends "); 1337 typeParam.append(typeSignatureToString(signature.substring(index), chopit)); 1338 index += unwrap(CONSUMER_CHARS); // update position 1339 } 1340 // look for interface bounds 1341 while (signature.charAt(index) == ':') { 1342 index++; // skip over the ':' 1343 typeParam.append(" & "); 1344 typeParam.append(typeSignatureToString(signature.substring(index), chopit)); 1345 index += unwrap(CONSUMER_CHARS); // update position 1346 } 1347 wrap(CONSUMER_CHARS, index); 1348 return typeParam.toString(); 1349 } 1350 1351 /** 1352 * Converts a list of type signatures to a string. 1353 * 1354 * @param signature signature to convert 1355 * @param chopit flag that determines whether chopping is executed or not 1356 * @param term character indicating the end of the list 1357 * @return String containg human readable signature 1358 */ 1359 private static String typeSignaturesToString(final String signature, final boolean chopit, final char term) { 1360 // The first character will be an 'open' that matches the 'close' contained in term. 1361 final StringBuilder typeList = new StringBuilder(signature.substring(0, 1)); 1362 int index = 1; // skip the 'open' character 1363 // get the first Type in the list 1364 if (signature.charAt(index) != term) { 1365 typeList.append(typeSignatureToString(signature.substring(index), chopit)); 1366 index += unwrap(CONSUMER_CHARS); // update position 1367 } 1368 // are there more types in the list? 1369 while (signature.charAt(index) != term) { 1370 typeList.append(", "); 1371 typeList.append(typeSignatureToString(signature.substring(index), chopit)); 1372 index += unwrap(CONSUMER_CHARS); // update position 1373 } 1374 wrap(CONSUMER_CHARS, index + 1); // account for the term char 1375 return typeList.append(term).toString(); 1376 } 1377 1378 /** 1379 * 1380 * This method converts a type signature string into a Java type declaration such as `String[]' and throws a 1381 * `ClassFormatException' when the parsed type is invalid. 1382 * 1383 * @param signature type signature 1384 * @param chopit flag that determines whether chopping is executed or not 1385 * @return string containing human readable type signature 1386 * @throws ClassFormatException if a class is malformed or cannot be interpreted as a class file 1387 * @since 6.4.0 1388 */ 1389 public static String typeSignatureToString(final String signature, final boolean chopit) throws ClassFormatException { 1390 // corrected concurrent private static field acess 1391 wrap(CONSUMER_CHARS, 1); // This is the default, read just one char like `B' 1392 try { 1393 switch (signature.charAt(0)) { 1394 case 'B': 1395 return "byte"; 1396 case 'C': 1397 return "char"; 1398 case 'D': 1399 return "double"; 1400 case 'F': 1401 return "float"; 1402 case 'I': 1403 return "int"; 1404 case 'J': 1405 return "long"; 1406 case 'T': { // TypeVariableSignature 1407 final int index = signature.indexOf(';'); // Look for closing `;' 1408 if (index < 0) { 1409 throw new ClassFormatException("Invalid type variable signature: " + signature); 1410 } 1411 // corrected concurrent private static field acess 1412 wrap(CONSUMER_CHARS, index + 1); // "Tblabla;" `T' and `;' are removed 1413 return compactClassName(signature.substring(1, index), chopit); 1414 } 1415 case 'L': { // Full class name 1416 // should this be a while loop? can there be more than 1417 // one generic clause? (markro) 1418 int fromIndex = signature.indexOf('<'); // generic type? 1419 if (fromIndex < 0) { 1420 fromIndex = 0; 1421 } else { 1422 fromIndex = signature.indexOf('>', fromIndex); 1423 if (fromIndex < 0) { 1424 throw new ClassFormatException("Invalid signature: " + signature); 1425 } 1426 } 1427 final int index = signature.indexOf(';', fromIndex); // Look for closing `;' 1428 if (index < 0) { 1429 throw new ClassFormatException("Invalid signature: " + signature); 1430 } 1431 1432 // check to see if there are any TypeArguments 1433 final int bracketIndex = signature.substring(0, index).indexOf('<'); 1434 if (bracketIndex < 0) { 1435 // just a class identifier 1436 wrap(CONSUMER_CHARS, index + 1); // "Lblabla;" `L' and `;' are removed 1437 return compactClassName(signature.substring(1, index), chopit); 1438 } 1439 // but make sure we are not looking past the end of the current item 1440 fromIndex = signature.indexOf(';'); 1441 if (fromIndex < 0) { 1442 throw new ClassFormatException("Invalid signature: " + signature); 1443 } 1444 if (fromIndex < bracketIndex) { 1445 // just a class identifier 1446 wrap(CONSUMER_CHARS, fromIndex + 1); // "Lblabla;" `L' and `;' are removed 1447 return compactClassName(signature.substring(1, fromIndex), chopit); 1448 } 1449 1450 // we have TypeArguments; build up partial result 1451 // as we recurse for each TypeArgument 1452 final StringBuilder type = new StringBuilder(compactClassName(signature.substring(1, bracketIndex), chopit)).append("<"); 1453 int consumed_chars = bracketIndex + 1; // Shadows global var 1454 1455 // check for wildcards 1456 if (signature.charAt(consumed_chars) == '+') { 1457 type.append("? extends "); 1458 consumed_chars++; 1459 } else if (signature.charAt(consumed_chars) == '-') { 1460 type.append("? super "); 1461 consumed_chars++; 1462 } 1463 1464 // get the first TypeArgument 1465 if (signature.charAt(consumed_chars) == '*') { 1466 type.append("?"); 1467 consumed_chars++; 1468 } else { 1469 type.append(typeSignatureToString(signature.substring(consumed_chars), chopit)); 1470 // update our consumed count by the number of characters the for type argument 1471 consumed_chars = unwrap(Utility.CONSUMER_CHARS) + consumed_chars; 1472 wrap(Utility.CONSUMER_CHARS, consumed_chars); 1473 } 1474 1475 // are there more TypeArguments? 1476 while (signature.charAt(consumed_chars) != '>') { 1477 type.append(", "); 1478 // check for wildcards 1479 if (signature.charAt(consumed_chars) == '+') { 1480 type.append("? extends "); 1481 consumed_chars++; 1482 } else if (signature.charAt(consumed_chars) == '-') { 1483 type.append("? super "); 1484 consumed_chars++; 1485 } 1486 if (signature.charAt(consumed_chars) == '*') { 1487 type.append("?"); 1488 consumed_chars++; 1489 } else { 1490 type.append(typeSignatureToString(signature.substring(consumed_chars), chopit)); 1491 // update our consumed count by the number of characters the for type argument 1492 consumed_chars = unwrap(Utility.CONSUMER_CHARS) + consumed_chars; 1493 wrap(Utility.CONSUMER_CHARS, consumed_chars); 1494 } 1495 } 1496 1497 // process the closing ">" 1498 consumed_chars++; 1499 type.append(">"); 1500 1501 if (signature.charAt(consumed_chars) == '.') { 1502 // we have a ClassTypeSignatureSuffix 1503 type.append("."); 1504 // convert SimpleClassTypeSignature to fake ClassTypeSignature 1505 // and then recurse to parse it 1506 type.append(typeSignatureToString("L" + signature.substring(consumed_chars + 1), chopit)); 1507 // update our consumed count by the number of characters the for type argument 1508 // note that this count includes the "L" we added, but that is ok 1509 // as it accounts for the "." we didn't consume 1510 consumed_chars = unwrap(Utility.CONSUMER_CHARS) + consumed_chars; 1511 wrap(Utility.CONSUMER_CHARS, consumed_chars); 1512 return type.toString(); 1513 } 1514 if (signature.charAt(consumed_chars) != ';') { 1515 throw new ClassFormatException("Invalid signature: " + signature); 1516 } 1517 wrap(Utility.CONSUMER_CHARS, consumed_chars + 1); // remove final ";" 1518 return type.toString(); 1519 } 1520 case 'S': 1521 return "short"; 1522 case 'Z': 1523 return "boolean"; 1524 case '[': { // Array declaration 1525 int n; 1526 StringBuilder brackets; 1527 String type; 1528 int consumed_chars; // Shadows global var 1529 brackets = new StringBuilder(); // Accumulate []'s 1530 // Count opening brackets and look for optional size argument 1531 for (n = 0; signature.charAt(n) == '['; n++) { 1532 brackets.append("[]"); 1533 } 1534 consumed_chars = n; // Remember value 1535 // The rest of the string denotes a `<field_type>' 1536 type = typeSignatureToString(signature.substring(n), chopit); 1537 // corrected concurrent private static field acess 1538 // Utility.consumed_chars += consumed_chars; is replaced by: 1539 final int temp = unwrap(Utility.CONSUMER_CHARS) + consumed_chars; 1540 wrap(Utility.CONSUMER_CHARS, temp); 1541 return type + brackets.toString(); 1542 } 1543 case 'V': 1544 return "void"; 1545 default: 1546 throw new ClassFormatException("Invalid signature: `" + signature + "'"); 1547 } 1548 } catch (final StringIndexOutOfBoundsException e) { // Should never occur 1549 throw new ClassFormatException("Invalid signature: " + signature, e); 1550 } 1551 } 1552 1553 private static int unwrap(final ThreadLocal<Integer> tl) { 1554 return tl.get().intValue(); 1555 } 1556 1557 private static void wrap(final ThreadLocal<Integer> tl, final int value) { 1558 tl.set(Integer.valueOf(value)); 1559 } 1560 1561}