content: /*
   *  This file is part of AndroidIDE.
   *
   *  AndroidIDE is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  AndroidIDE is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *   along with AndroidIDE.  If not, see <https://www.gnu.org/licenses/>.
   */
  
  /*
   * reserved comment block
   * DO NOT REMOVE OR ALTER!
   */
  package jaxp.sun.org.apache.bcel.internal.classfile;
  
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" and
   *    "Apache BCEL" must not be used to endorse or promote products
   *    derived from this software without prior written permission. For
   *    written permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    "Apache BCEL", nor may "Apache" appear in their name, without
   *    prior written permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  import jaxp.sun.org.apache.bcel.internal.Constants;
  import jaxp.sun.org.apache.bcel.internal.util.ByteSequence;
  
  import java.io.*;
  import java.util.ArrayList;
  import java.util.zip.*;
  
  /**
   * Utility functions that do not really belong to any class in particular.
   *
   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
   */
  public abstract class Utility {
    private static int consumed_chars; /* How many chars have been consumed
                                        * during parsing in signatureToString().
                                        * Read by methodSignatureToString().
                                        * Set by side effect,but only internally.
                                        */
    private static boolean wide=false; /* The `WIDE' instruction is used in the
                                        * byte code to allow 16-bit wide indices
                                        * for local variables. This opcode
                                        * precedes an `ILOAD', e.g.. The opcode
                                        * immediately following takes an extra
                                        * byte which is combined with the
                                        * following byte to form a
                                        * 16-bit value.
                                        */
    /**
     * Convert bit field of flags into string such as `static final'.
     *
     * @param  access_flags Access flags
     * @return String representation of flags
     */
    public static final String accessToString(int access_flags) {
      return accessToString(access_flags, false);
    }
  
    /**
     * Convert bit field of flags into string such as `static final'.
     *
     * Special case: Classes compiled with new compilers and with the
     * `ACC_SUPER' flag would be said to be "synchronized". This is
     * because SUN used the same value for the flags `ACC_SUPER' and
     * `ACC_SYNCHRONIZED'.
     *
     * @param  access_flags Access flags
     * @param  for_class access flags are for class qualifiers ?
     * @return String representation of flags
     */
    public static final String accessToString(int access_flags,
                                              boolean for_class)
    {
      StringBuffer buf = new StringBuffer();
  
      int p = 0;
      for(int i = 0; p < Constants.MAX_ACC_FLAG; i++) { // Loop through known flags
        p = pow2(i);
  
        if((access_flags & p) != 0) {
          /* Special case: Classes compiled with new compilers and with the
           * `ACC_SUPER' flag would be said to be "synchronized". This is
           * because SUN used the same value for the flags `ACC_SUPER' and
           * `ACC_SYNCHRONIZED'.
           */
          if(for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE)))
            continue;
  
          buf.append(Constants.ACCESS_NAMES[i] + " ");
        }
      }
  
      return buf.toString().trim();
    }
  
    /**
     * @return "class" or "interface", depending on the ACC_INTERFACE flag
     */
    public static final String classOrInterface(int access_flags) {
      return ((access_flags & Constants.ACC_INTERFACE) != 0)? "interface" : "class";
    }
  
    /**
     * Disassemble a byte array of JVM byte codes starting from code line
     * `index' and return the disassembled string representation. Decode only
     * `num' opcodes (including their operands), use -1 if you want to
     * decompile everything.
     *
     * @param  code byte code array
     * @param  constant_pool Array of constants
     * @param  index offset in `code' array
     * <EM>(number of opcodes, not bytes!)</EM>
     * @param  length number of opcodes to decompile, -1 for all
     * @param  verbose be verbose, e.g. print constant pool index
     * @return String representation of byte codes
     */
    public static final String codeToString(byte[] code,
                                            ConstantPool constant_pool,
                                            int index, int length, boolean verbose)
    {
      StringBuffer buf    = new StringBuffer(code.length * 20); // Should be sufficient
      ByteSequence stream = new ByteSequence(code);
  
      try {
        for(int i=0; i < index; i++) // Skip `index' lines of code
          codeToString(stream, constant_pool, verbose);
  
        for(int i=0; stream.available() > 0; i++) {
          if((length < 0) || (i < length)) {
            String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
            buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
          }
        }
      } catch(IOException e) {
        System.out.println(buf.toString());
        e.printStackTrace();
        throw new ClassFormatException("Byte code error: " + e);
      }
  
      return buf.toString();
    }
  
    public static final String codeToString(byte[] code,
                                            ConstantPool constant_pool,
                                            int index, int length) {
      return codeToString(code, constant_pool, index, length, true);
    }
  
    /**
     * Disassemble a stream of byte codes and return the
     * string representation.
     *
     * @param  bytes stream of bytes
     * @param  constant_pool Array of constants
     * @param  verbose be verbose, e.g. print constant pool index
     * @return String representation of byte code
     */
    public static final String codeToString(ByteSequence bytes,
                                            ConstantPool constant_pool, boolean verbose)
         throws IOException
    {
      short        opcode = (short)bytes.readUnsignedByte();
      int          default_offset=0, low, high, npairs;
      int          index, vindex, constant;
      int[]        match, jump_table;
      int          no_pad_bytes=0, offset;
      StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
  
      /* Special case: Skip (0-3) padding bytes, i.e., the
       * following bytes are 4-byte-aligned
       */
      if((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
        int remainder = bytes.getIndex() % 4;
        no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
  
        for(int i=0; i < no_pad_bytes; i++) {
          byte b;
  
          if((b=bytes.readByte()) != 0)
            System.err.println("Warning: Padding byte != 0 in " +
                               Constants.OPCODE_NAMES[opcode] + ":" + b);
        }
  
        // Both cases have a field default_offset in common
        default_offset = bytes.readInt();
      }
  
      switch(opcode) {
        /* Table switch has variable length arguments.
         */
      case Constants.TABLESWITCH:
        low  = bytes.readInt();
        high = bytes.readInt();
  
        offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
        default_offset += offset;
  
        buf.append("\tdefault = " + default_offset + ", low = " + low +
                   ", high = " + high + "(");
  
        jump_table = new int[high - low + 1];
        for(int i=0; i < jump_table.length; i++) {
          jump_table[i] = offset + bytes.readInt();
          buf.append(jump_table[i]);
  
          if(i < jump_table.length - 1)
            buf.append(", ");
        }
        buf.append(")");
  
        break;
  
        /* Lookup switch has variable length arguments.
         */
      case Constants.LOOKUPSWITCH: {
  
        npairs = bytes.readInt();
        offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
  
        match      = new int[npairs];
        jump_table = new int[npairs];
        default_offset += offset;
  
        buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
                   " (");
  
        for(int i=0; i < npairs; i++) {
          match[i]      = bytes.readInt();
  
          jump_table[i] = offset + bytes.readInt();
  
          buf.append("(" + match[i] + ", " + jump_table[i] + ")");
  
          if(i < npairs - 1)
            buf.append(", ");
        }
        buf.append(")");
      }
      break;
  
      /* Two address bytes + offset from start of byte stream form the
       * jump target
       */
      case Constants.GOTO:      case Constants.IFEQ:      case Constants.IFGE:      case Constants.IFGT:
      case Constants.IFLE:      case Constants.IFLT:      case Constants.JSR: case Constants.IFNE:
      case Constants.IFNONNULL: case Constants.IFNULL:    case Constants.IF_ACMPEQ:
      case Constants.IF_ACMPNE: case Constants.IF_ICMPEQ: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT:
      case Constants.IF_ICMPLE: case Constants.IF_ICMPLT: case Constants.IF_ICMPNE:
        buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
        break;
  
        /* 32-bit wide jumps
         */
      case Constants.GOTO_W: case Constants.JSR_W:
        buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
        break;
  
        /* Index byte references local variable (register)
         */
      case Constants.ALOAD:  case Constants.ASTORE: case Constants.DLOAD:  case Constants.DSTORE: case Constants.FLOAD:
      case Constants.FSTORE: case Constants.ILOAD:  case Constants.ISTORE: case Constants.LLOAD:  case Constants.LSTORE:
      case Constants.RET:
        if(wide) {
          vindex = bytes.readUnsignedShort();
          wide=false; // Clear flag
        }
        else
          vindex = bytes.readUnsignedByte();
  
        buf.append("\t\t%" + vindex);
        break;
  
        /*
         * Remember wide byte which is used to form a 16-bit address in the
         * following instruction. Relies on that the method is called again with
         * the following opcode.
         */
      case Constants.WIDE:
        wide      = true;
        buf.append("\t(wide)");
        break;
  
        /* Array of basic type.
         */
      case Constants.NEWARRAY:
        buf.append("\t\t<" + Constants.TYPE_NAMES[bytes.readByte()] + ">");
        break;
  
        /* Access object/class fields.
         */
      case Constants.GETFIELD: case Constants.GETSTATIC: case Constants.PUTFIELD: case Constants.PUTSTATIC:
        index = bytes.readUnsignedShort();
        buf.append("\t\t" +
                   constant_pool.constantToString(index, Constants.CONSTANT_Fieldref) +
                   (verbose? " (" + index + ")" : ""));
        break;
  
        /* Operands are references to classes in constant pool
         */
      case Constants.NEW:
      case Constants.CHECKCAST:
        buf.append("\t");
      case Constants.INSTANCEOF:
        index = bytes.readUnsignedShort();
        buf.append("\t<" + constant_pool.constantToString(index,
                                                          Constants.CONSTANT_Class) +
                   ">" + (verbose? " (" + index + ")" : ""));
        break;
  
        /* Operands are references to methods in constant pool
         */
      case Constants.INVOKESPECIAL: case Constants.INVOKESTATIC: case Constants.INVOKEVIRTUAL:
        index = bytes.readUnsignedShort();
        buf.append("\t" + constant_pool.constantToString(index,
                                                         Constants.CONSTANT_Methodref) +
                   (verbose? " (" + index + ")" : ""));
        break;
  
      case Constants.INVOKEINTERFACE:
        index = bytes.readUnsignedShort();
        int nargs = bytes.readUnsignedByte(); // historical, redundant
        buf.append("\t" +
                   constant_pool.constantToString(index,
                                                  Constants.CONSTANT_InterfaceMethodref) +
                   (verbose? " (" + index + ")\t" : "") + nargs + "\t" +
                   bytes.readUnsignedByte()); // Last byte is a reserved space
        break;
  
        /* Operands are references to items in constant pool
         */
      case Constants.LDC_W: case Constants.LDC2_W:
        index = bytes.readUnsignedShort();
  
        buf.append("\t\t" + constant_pool.constantToString
                   (index, constant_pool.getConstant(index).getTag()) +
                   (verbose? " (" + index + ")" : ""));
        break;
  
      case Constants.LDC:
        index = bytes.readUnsignedByte();
  
        buf.append("\t\t" +
                   constant_pool.constantToString
                   (index, constant_pool.getConstant(index).getTag()) +
                   (verbose? " (" + index + ")" : ""));
        break;
  
        /* Array of references.
         */
      case Constants.ANEWARRAY:
        index = bytes.readUnsignedShort();
  
        buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
                                            (index, Constants.CONSTANT_Class), false) +
                   ">" + (verbose? " (" + index + ")": ""));
        break;
  
        /* Multidimensional array of references.
         */
      case Constants.MULTIANEWARRAY: {
        index          = bytes.readUnsignedShort();
        int dimensions = bytes.readUnsignedByte();
  
        buf.append("\t<" + compactClassName(constant_pool.getConstantString
                                            (index, Constants.CONSTANT_Class), false) +
                   ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
      }
      break;
  
      /* Increment local variable.
       */
      case Constants.IINC:
        if(wide) {
          vindex   = bytes.readUnsignedShort();
          constant = bytes.readShort();
          wide     = false;
        }
        else {
          vindex   = bytes.readUnsignedByte();
          constant = bytes.readByte();
        }
        buf.append("\t\t%" + vindex + "\t" + constant);
        break;
  
      default:
        if(Constants.NO_OF_OPERANDS[opcode] > 0) {
          for(int i=0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
            buf.append("\t\t");
            switch(Constants.TYPE_OF_OPERANDS[opcode][i]) {
            case Constants.T_BYTE:  buf.append(bytes.readByte()); break;
            case Constants.T_SHORT: buf.append(bytes.readShort());       break;
            case Constants.T_INT:   buf.append(bytes.readInt());         break;
  
            default: // Never reached
              System.err.println("Unreachable default case reached!");
              buf.setLength(0);
            }
          }
        }
      }
  
      return buf.toString();
    }
  
    public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
      throws IOException
    {
      return codeToString(bytes, constant_pool, true);
    }
  
    /**
     * Shorten long class names, <em>java/lang/String</em> becomes
     * <em>String</em>.
     *
     * @param str The long class name
     * @return Compacted class name
     */
    public static final String compactClassName(String str) {
      return compactClassName(str, true);
    }
  
    /**
     * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
     * if the
     * class name starts with this string and the flag <em>chopit</em> is true.
     * Slashes <em>/</em> are converted to dots <em>.</em>.
     *
     * @param str The long class name
     * @param prefix The prefix the get rid off
     * @param chopit Flag that determines whether chopping is executed or not
     * @return Compacted class name
     */
    public static final String compactClassName(String str,
                                                String prefix,
                                                boolean chopit)
    {
      int len = prefix.length();
  
      str = str.replace('/', '.'); // Is `/' on all systems, even DOS
  
      if(chopit) {
        // If string starts with `prefix' and contains no further dots
        if(str.startsWith(prefix) &&
           (str.substring(len).indexOf('.') == -1))
          str = str.substring(len);
      }
  
      return str;
    }
  
    /**
     * Shorten long class names, <em>java/lang/String</em> becomes
     * <em>java.lang.String</em>,
     * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
     * is also removed.
     *
     * @param str The long class name
     * @param chopit Flag that determines whether chopping is executed or not
     * @return Compacted class name
     */
    public static final String compactClassName(String str, boolean chopit) {
      return compactClassName(str, "java.lang.", chopit);
    }
  
    private static final boolean is_digit(char ch) {
      return (ch >= '0') && (ch <= '9');
    }
  
    private static final boolean is_space(char ch) {
      return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
    }
  
    /**
     * @return `flag' with bit `i' set to 1
     */
    public static final int setBit(int flag, int i) {
      return flag | pow2(i);
    }
  
    /**
     * @return `flag' with bit `i' set to 0
     */
    public static final int clearBit(int flag, int i) {
      int bit = pow2(i);
      return (flag & bit) == 0? flag : flag ^ bit;
    }
  
    /**
     * @return true, if bit `i' in `flag' is set
     */
    public static final boolean isSet(int flag, int i) {
      return (flag & pow2(i)) != 0;
    }
  
    /**
     * Converts string containing the method return and argument types
     * to a byte code method signature.
     *
     * @param  ret Return type of method
     * @param  argv Types of method arguments
     * @return Byte code representation of method signature
     */
    public final static String methodTypeToSignature(String ret, String[] argv)
      throws ClassFormatException
    {
      StringBuffer buf = new StringBuffer("(");
      String       str;
  
      if(argv != null)
        for(int i=0; i < argv.length; i++) {
          str = getSignature(argv[i]);
  
          if(str.endsWith("V")) // void can't be a method argument
            throw new ClassFormatException("Invalid type: " + argv[i]);
  
          buf.append(str);
        }
  
      str = getSignature(ret);
  
      buf.append(")" + str);
  
      return buf.toString();
    }
  
    /**
     * @param  signature    Method signature
     * @return Array of argument types
     * @throws  ClassFormatException
     */
    public static final String[] methodSignatureArgumentTypes(String signature)
      throws ClassFormatException
    {
      return methodSignatureArgumentTypes(signature, true);
    }
  
    /**
     * @param  signature    Method signature
     * @param chopit Shorten class names ?
     * @return Array of argument types
     * @throws  ClassFormatException
     */
    public static final String[] methodSignatureArgumentTypes(String signature,
                                                              boolean chopit)
      throws ClassFormatException
    {
      ArrayList vec = new ArrayList();
      int       index;
      String[]  types;
  
      try { // Read all declarations between for `(' and `)'
        if(signature.charAt(0) != '(')
          throw new ClassFormatException("Invalid method signature: " + signature);
  
        index = 1; // current string position
  
        while(signature.charAt(index) != ')') {
          vec.add(signatureToString(signature.substring(index), chopit));
          index += consumed_chars; // update position
        }
      } catch(StringIndexOutOfBoundsException e) { // Should never occur
        throw new ClassFormatException("Invalid method signature: " + signature);
      }
  
      types = new String[vec.size()];
      vec.toArray(types);
      return types;
    }
    /**
     * @param  signature    Method signature
     * @return return type of method
     * @throws  ClassFormatException
     */
    public static final String methodSignatureReturnType(String signature)
         throws ClassFormatException
    {
      return methodSignatureReturnType(signature, true);
    }
    /**
     * @param  signature    Method signature
     * @param chopit Shorten class names ?
     * @return return type of method
     * @throws  ClassFormatException
     */
    public static final String methodSignatureReturnType(String signature,
                                                         boolean chopit)
         throws ClassFormatException
    {
      int    index;
      String type;
  
      try {
        // Read return type after `)'
        index = signature.lastIndexOf(')') + 1;
        type = signatureToString(signature.substring(index), chopit);
      } catch(StringIndexOutOfBoundsException e) { // Should never occur
        throw new ClassFormatException("Invalid method signature: " + signature);
      }
  
      return type;
    }
  
    /**
     * Converts method signature to string with all class names compacted.
     *
     * @param signature to convert
     * @param name of method
     * @param access flags of method
     * @return Human readable signature
     */
    public static final String methodSignatureToString(String signature,
                                                       String name,
                                                       String access) {
      return methodSignatureToString(signature, name, access, true);
    }
  
    public static final String methodSignatureToString(String signature,
                                                       String name,
                                                       String access,
                                                       boolean chopit) {
      return methodSignatureToString(signature, name, access, chopit, null);
    }
  
    /**
     * A return type signature represents the return value from a method.
     * It is a series of bytes in the following grammar:
     *
     * <return_signature> ::= <field_type> | V
     *
     * The character V indicates that the method returns no value. Otherwise, the
     * signature indicates the type of the return value.
     * An argument signature represents an argument passed to a method:
     *
     * <argument_signature> ::= <field_type>
     *
     * A method signature represents the arguments that the method expects, and
     * the value that it returns.
     * <method_signature> ::= (<arguments_signature>) <return_signature>
     * <arguments_signature>::= <argument_signature>*
     *
     * This method converts such a string into a Java type declaration like
     * `void _main(String[])' and throws a `ClassFormatException' when the parsed
     * type is invalid.
     *
     * @param  signature    Method signature
     * @param  name         Method name
     * @param  access       Method access rights
     * @return Java type declaration
     * @throws  ClassFormatException
     */
    public static final String methodSignatureToString(String signature,
                                                       String name,
                                                       String access,
                                                       boolean chopit,
                                                       LocalVariableTable vars)
      throws ClassFormatException
    {
      StringBuffer buf = new StringBuffer("(");
      String       type;
      int          index;
      int          var_index = (access.indexOf("static") >= 0)? 0 : 1;
  
      try { // Read all declarations between for `(' and `)'
        if(signature.charAt(0) != '(')
          throw new ClassFormatException("Invalid method signature: " + signature);
  
        index = 1; // current string position
  
        while(signature.charAt(index) != ')') {
          String param_type = signatureToString(signature.substring(index), chopit);
          buf.append(param_type);
  
          if(vars != null) {
            LocalVariable l = vars.getLocalVariable(var_index);
  
            if(l != null)
              buf.append(" " + l.getName());
          } else
            buf.append(" arg" + var_index);
  
          if("double".equals(param_type) || "long".equals(param_type))
            var_index += 2;
          else
            var_index++;
  
          buf.append(", ");
          index += consumed_chars; // update position
        }
  
        index++; // update position
  
        // Read return type after `)'
        type = signatureToString(signature.substring(index), chopit);
  
      } catch(StringIndexOutOfBoundsException e) { // Should never occur
        throw new ClassFormatException("Invalid method signature: " + signature);
      }
  
      if(buf.length() > 1) // Tack off the extra ", "
        buf.setLength(buf.length() - 2);
  
      buf.append(")");
  
      return access + ((access.length() > 0)? " " : "") + // May be an empty string
        type + " " + name + buf.toString();
    }
  
    // Guess what this does
    private static final int pow2(int n) {
      return 1 << n;
    }
  
    /**
     * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
     *
     * @param str String to permute
     * @param old String to be replaced
     * @param new Replacement string
     * @return new String object
     */
    public static final String replace(String str, String old, String new_) {
      int          index, old_index;
      StringBuffer buf = new StringBuffer();
  
      try {
        if((index = str.indexOf(old)) != -1) { // `old' found in str
          old_index = 0;                       // String start offset
  
          // While we have something to replace
          while((index = str.indexOf(old, old_index)) != -1) {
            buf.append(str.substring(old_index, index)); // append prefix
            buf.append(new_);                            // append replacement
  
            old_index = index + old.length(); // Skip `old'.length chars
          }
  
          buf.append(str.substring(old_index)); // append rest of string
          str = buf.toString();
        }
      } catch(StringIndexOutOfBoundsException e) { // Should not occur
        System.err.println(e);
      }
  
      return str;
    }
  
    /**
     * Converts signature to string with all class names compacted.
     *
     * @param signature to convert
     * @return Human readable signature
     */
    public static final String signatureToString(String signature) {
      return signatureToString(signature, true);
    }
  
    /**
     * The field signature represents the value of an argument to a function or
     * the value of a variable. It is a series of bytes generated by the
     * following grammar:
     *
     * <PRE>
     * <field_signature> ::= <field_type>
     * <field_type>      ::= <base_type>|<object_type>|<array_type>
     * <base_type>       ::= B|C|D|F|I|J|S|Z
     * <object_type>     ::= L<fullclassname>;
     * <array_type>      ::= [<field_type>
     *
     * The meaning of the base types is as follows:
     * B byte signed byte
     * C char character
     * D double double precision IEEE float
     * F float single precision IEEE float
     * I int integer
     * J long long integer
     * L<fullclassname>; ... an object of the given class
     * S short signed short
     * Z boolean true or false
     * [<field sig> ... array
     * </PRE>
     *
     * This method converts this string into a Java type declaration such as
     * `String[]' and throws a `ClassFormatException' when the parsed type is
     * invalid.
     *
     * @param  signature  Class signature
     * @param chopit Flag that determines whether chopping is executed or not
     * @return Java type declaration
     * @throws ClassFormatException
     */
    public static final String signatureToString(String signature,
                                                 boolean chopit)
    {
      consumed_chars = 1; // This is the default, read just one char like `B'
  
      try {
        switch(signature.charAt(0)) {
        case 'B' : return "byte";
        case 'C' : return "char";
        case 'D' : return "double";
        case 'F' : return "float";
        case 'I' : return "int";
        case 'J' : return "long";
  
        case 'L' : { // Full class name
          int    index = signature.indexOf(';'); // Look for closing `;'
  
          if(index < 0)
            throw new ClassFormatException("Invalid signature: " + signature);
  
          consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
  
          return compactClassName(signature.substring(1, index), chopit);
        }
  
        case 'S' : return "short";
        case 'Z' : return "boolean";
  
        case '[' : { // Array declaration
          int          n;
          StringBuffer buf, brackets;
          String       type;
          char         ch;
          int          consumed_chars; // Shadows global var
  
          brackets = new StringBuffer(); // Accumulate []'s
  
          // Count opening brackets and look for optional size argument
          for(n=0; signature.charAt(n) == '['; n++)
            brackets.append("[]");
  
          consumed_chars = n; // Remember value
  
          // The rest of the string denotes a `<field_type>'
          type = signatureToString(signature.substring(n), chopit);
  
          Utility.consumed_chars += consumed_chars;
          return type + brackets.toString();
        }
  
        case 'V' : return "void";
  
        default  : throw new ClassFormatException("Invalid signature: `" +
                                              signature + "'");
        }
      } catch(StringIndexOutOfBoundsException e) { // Should never occur
        throw new ClassFormatException("Invalid signature: " + e + ":" + signature);
      }
    }
  
    /** Parse Java type such as "char", or "java.lang.String[]" and return the
     * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
     *
     * @param  type Java type
     * @return byte code signature
     */
    public static String getSignature(String type) {
      StringBuffer buf        = new StringBuffer();
      char[]       chars      = type.toCharArray();
      boolean      char_found = false, delim = false;
      int          index      = -1;
  
    loop:
      for(int i=0; i < chars.length; i++) {
        switch(chars[i]) {
        case ' ': case '\t': case '\n': case '\r': case '\f':
          if(char_found)
            delim = true;
          break;
  
        case '[':
          if(!char_found)
            throw new RuntimeException("Illegal type: " + type);
  
          index = i;
          break loop;
  
        default:
          char_found = true;
          if(!delim)
            buf.append(chars[i]);
        }
      }
  
      int brackets = 0;
  
      if(index > 0)
        brackets = countBrackets(type.substring(index));
  
      type = buf.toString();
      buf.setLength(0);
  
      for(int i=0; i < brackets; i++)
        buf.append('[');
  
      boolean found = false;
  
      for(int i=Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) {
        if(Constants.TYPE_NAMES[i].equals(type)) {
          found = true;
          buf.append(Constants.SHORT_TYPE_NAMES[i]);
        }
      }
  
      if(!found) // Class name
        buf.append('L' + type.replace('.', '/') + ';');
  
      return buf.toString();
    }
  
    private static int countBrackets(String brackets) {
      char[]  chars = brackets.toCharArray();
      int     count = 0;
      boolean open  = false;
  
      for(int i=0; i<chars.length; i++) {
        switch(chars[i]) {
        case '[':
          if(open)
            throw new RuntimeException("Illegally nested brackets:" + brackets);
          open = true;
          break;
  
        case ']':
          if(!open)
            throw new RuntimeException("Illegally nested brackets:" + brackets);
          open = false;
          count++;
          break;
  
        default:
          // Don't care
        }
      }
  
      if(open)
        throw new RuntimeException("Illegally nested brackets:" + brackets);
  
      return count;
    }
  
    /**
     * Return type of method signature as a byte value as defined in <em>Constants</em>
     *
     * @param  signature in format described above
     * @return type of method signature
     * @see    Constants
     */
    public static final byte typeOfMethodSignature(String signature)
      throws ClassFormatException
    {
      int index;
  
      try {
        if(signature.charAt(0) != '(')
          throw new ClassFormatException("Invalid method signature: " + signature);
  
        index = signature.lastIndexOf(')') + 1;
        return typeOfSignature(signature.substring(index));
      } catch(StringIndexOutOfBoundsException e) {
        throw new ClassFormatException("Invalid method signature: " + signature);
      }
    }
  
    /**
     * Return type of signature as a byte value as defined in <em>Constants</em>
     *
     * @param  signature in format described above
     * @return type of signature
     * @see    Constants
     */
    public static final byte typeOfSignature(String signature)
      throws ClassFormatException
    {
      try {
        switch(signature.charAt(0)) {
        case 'B' : return Constants.T_BYTE;
        case 'C' : return Constants.T_CHAR;
        case 'D' : return Constants.T_DOUBLE;
        case 'F' : return Constants.T_FLOAT;
        case 'I' : return Constants.T_INT;
        case 'J' : return Constants.T_LONG;
        case 'L' : return Constants.T_REFERENCE;
        case '[' : return Constants.T_ARRAY;
        case 'V' : return Constants.T_VOID;
        case 'Z' : return Constants.T_BOOLEAN;
        case 'S' : return Constants.T_SHORT;
        default:
          throw new ClassFormatException("Invalid method signature: " + signature);
        }
      } catch(StringIndexOutOfBoundsException e) {
        throw new ClassFormatException("Invalid method signature: " + signature);
      }
    }
  
    /** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
     */
    public static short searchOpcode(String name) {
      name = name.toLowerCase();
  
      for(short i=0; i < Constants.OPCODE_NAMES.length; i++)
        if(Constants.OPCODE_NAMES[i].equals(name))
          return i;
  
      return -1;
    }
  
    /**
     * Convert (signed) byte to (unsigned) short value, i.e., all negative
     * values become positive.
     */
    private static final short byteToShort(byte b) {
      return (b < 0)? (short)(256 + b) : (short)b;
    }
  
    /** Convert bytes into hexidecimal string
     *
     * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
     */
    public static final String toHexString(byte[] bytes) {
      StringBuffer buf = new StringBuffer();
  
      for(int i=0; i < bytes.length; i++) {
        short  b   = byteToShort(bytes[i]);
        String hex = Integer.toString(b, 0x10);
  
        if(b < 0x10) // just one digit, prepend '0'
          buf.append('0');
  
        buf.append(hex);
  
        if(i < bytes.length - 1)
          buf.append(' ');
      }
  
      return buf.toString();
    }
  
    /**
     * Return a string for an integer justified left or right and filled up with
     * `fill' characters if necessary.
     *
     * @param i integer to format
     * @param length length of desired string
     * @param left_justify format left or right
     * @param fill fill character
     * @return formatted int
     */
    public static final String format(int i, int length, boolean left_justify, char fill) {
      return fillup(Integer.toString(i), length, left_justify, fill);
    }
  
    /**
     * Fillup char with up to length characters with char `fill' and justify it left or right.
     *
     * @param str string to format
     * @param length length of desired string
     * @param left_justify format left or right
     * @param fill fill character
     * @return formatted string
     */
    public static final String fillup(String str, int length, boolean left_justify, char fill) {
      int    len = length - str.length();
      char[] buf = new char[(len < 0)? 0 : len];
  
      for(int j=0; j < buf.length; j++)
        buf[j] = fill;
  
      if(left_justify)
        return str + new String(buf);
      else
        return new String(buf) + str;
    }
  
    static final boolean equals(byte[] a, byte[] b) {
      int size;
  
      if((size=a.length) != b.length)
        return false;
  
      for(int i=0; i < size; i++)
        if(a[i] != b[i])
          return false;
  
      return true;
    }
  
    public static final void printArray(PrintStream out, Object[] obj) {
      out.println(printArray(obj, true));
    }
  
    public static final void printArray(PrintWriter out, Object[] obj) {
      out.println(printArray(obj, true));
    }
  
    public static final String printArray(Object[] obj) {
      return printArray(obj, true);
    }
  
    public static final String printArray(Object[] obj, boolean braces) {
      return printArray(obj, braces, false);
    }
  
    public static final String printArray(Object[] obj, boolean braces,
                                          boolean quote) {
      if(obj == null)
        return null;
  
      StringBuffer buf = new StringBuffer();
      if(braces)
        buf.append('{');
  
      for(int i=0; i < obj.length; i++) {
        if(obj[i] != null) {
          buf.append((quote? "\"" : "") + obj[i].toString() + (quote? "\"" : ""));
        } else {
          buf.append("null");
        }
  
        if(i < obj.length - 1) {
          buf.append(", ");
        }
      }
  
      if(braces)
        buf.append('}');
  
      return buf.toString();
    }
  
    /** @return true, if character is one of (a, ... z, A, ... Z, 0, ... 9, _)
     */
    public static boolean isJavaIdentifierPart(char ch) {
      return ((ch >= 'a') && (ch <= 'z')) ||
        ((ch >= 'A') && (ch <= 'Z')) ||
        ((ch >= '0') && (ch <= '9')) ||
        (ch == '_');
    }
  
    /** Encode byte array it into Java identifier string, i.e., a string
     * that only contains the following characters: (a, ... z, A, ... Z,
     * 0, ... 9, _, $).  The encoding algorithm itself is not too
     * clever: if the current byte's ASCII value already is a valid Java
     * identifier part, leave it as it is. Otherwise it writes the
     * escape character($) followed by <p><ul><li> the ASCII value as a
     * hexadecimal string, if the value is not in the range
     * 200..247</li> <li>a Java identifier char not used in a lowercase
     * hexadecimal string, if the value is in the range
     * 200..247</li><ul></p>
     *
     * <p>This operation inflates the original byte array by roughly 40-50%</p>
     *
     * @param bytes the byte array to convert
     * @param compress use gzip to minimize string
     */
    public static String encode(byte[] bytes, boolean compress) throws IOException {
      if(compress) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream      gos  = new GZIPOutputStream(baos);
  
        gos.write(bytes, 0, bytes.length);
        gos.close();
        baos.close();
  
        bytes = baos.toByteArray();
      }
  
      CharArrayWriter caw = new CharArrayWriter();
      JavaWriter      jw  = new JavaWriter(caw);
  
      for(int i=0; i < bytes.length; i++) {
        int in = bytes[i] & 0x000000ff; // Normalize to unsigned
        jw.write(in);
      }
  
      return caw.toString();
    }
  
    /** Decode a string back to a byte array.
     *
     * @param bytes the byte array to convert
     * @param uncompress use gzip to uncompress the stream of bytes
     */
    public static byte[] decode(String s, boolean uncompress) throws IOException {
      char[] chars = s.toCharArray();
  
      CharArrayReader car = new CharArrayReader(chars);
      JavaReader      jr  = new JavaReader(car);
  
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
  
      int ch;
  
      while((ch = jr.read()) >= 0) {
        bos.write(ch);
      }
  
      bos.close();
      car.close();
      jr.close();
  
      byte[] bytes = bos.toByteArray();
  
      if(uncompress) {
        GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes));
  
        byte[] tmp   = new byte[bytes.length * 3]; // Rough estimate
        int    count = 0;
        int    b;
  
        while((b = gis.read()) >= 0)
          tmp[count++] = (byte)b;
  
        bytes = new byte[count];
        System.arraycopy(tmp, 0, bytes, 0, count);
      }
  
      return bytes;
    }
  
    // A-Z, g-z, _, $
    private static final int   FREE_CHARS  = 48;
    private static       int[] CHAR_MAP    = new int[FREE_CHARS];
    private static       int[] MAP_CHAR    = new int[256]; // Reverse map
    private static final char  ESCAPE_CHAR = '$';
  
    static {
      int j = 0, k = 0;
      for(int i='A'; i <= 'Z'; i++) {
        CHAR_MAP[j] = i;
        MAP_CHAR[i] = j;
        j++;
      }
  
      for(int i='g'; i <= 'z'; i++) {
        CHAR_MAP[j] = i;
        MAP_CHAR[i] = j;
        j++;
      }
  
      CHAR_MAP[j]   = '$';
      MAP_CHAR['$'] = j;
      j++;
  
      CHAR_MAP[j]   = '_';
      MAP_CHAR['_'] = j;
    }
  
    /** Decode characters into bytes.
     * Used by <a href="Utility.html#decode(java.lang.String, boolean)">decode()</a>
     */
    private static class JavaReader extends FilterReader {
      public JavaReader(Reader in) {
        super(in);
      }
  
      public int read() throws IOException {
        int b = in.read();
  
        if(b != ESCAPE_CHAR) {
          return b;
        } else {
          int i = in.read();
  
          if(i < 0)
            return -1;
  
          if(((i >= '0') && (i <= '9')) || ((i >= 'a') && (i <= 'f'))) { // Normal escape
            int j = in.read();
  
            if(j < 0)
              return -1;
  
            char[] tmp = { (char)i, (char)j };
            int    s   = Integer.parseInt(new String(tmp), 16);
  
            return s;
          } else { // Special escape
            return MAP_CHAR[i];
          }
        }
      }
  
      public int read(char[] cbuf, int off, int len) throws IOException {
        for(int i=0; i < len; i++)
          cbuf[off + i] = (char)read();
  
        return len;
      }
    }
  
    /** Encode bytes into valid java identifier characters.
     * Used by <a href="Utility.html#encode(byte[], boolean)">encode()</a>
     */
    private static class JavaWriter extends FilterWriter {
      public JavaWriter(Writer out) {
        super(out);
      }
  
      public void write(int b) throws IOException {
        if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
          out.write(b);
        } else {
          out.write(ESCAPE_CHAR); // Escape character
  
          // Special escape
          if(b >= 0 && b < FREE_CHARS) {
            out.write(CHAR_MAP[b]);
          } else { // Normal escape
            char[] tmp = Integer.toHexString(b).toCharArray();
  
            if(tmp.length == 1) {
              out.write('0');
              out.write(tmp[0]);
            } else {
              out.write(tmp[0]);
              out.write(tmp[1]);
            }
          }
        }
      }
  
      public void write(char[] cbuf, int off, int len) throws IOException {
        for(int i=0; i < len; i++)
          write(cbuf[off + i]);
      }
  
      public void write(String str, int off, int len) throws IOException {
        write(str.toCharArray(), off, len);
      }
    }
  
    /**
     * Escape all occurences of newline chars '\n', quotes \", etc.
     */
    public static final String convertString(String label) {
      char[]       ch  = label.toCharArray();
      StringBuffer buf = new StringBuffer();
  
      for(int i=0; i < ch.length; i++) {
        switch(ch[i]) {
        case '\n':
          buf.append("\\n"); break;
        case '\r':
          buf.append("\\r"); break;
        case '\"':
          buf.append("\\\""); break;
        case '\'':
          buf.append("\\'"); break;
        case '\\':
          buf.append("\\\\"); break;
        default:
          buf.append(ch[i]); break;
        }
      }
  
      return buf.toString();
    }
  }
  