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