View Javadoc

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