    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 returntype 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