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

This page was automatically generated by Maven