Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

Utility.java

00001 package de.fub.bytecode.classfile;
00002 
00003 import de.fub.bytecode.Constants;
00004 import de.fub.bytecode.util.ByteSequence;
00005 import java.io.*;
00006 import java.util.Vector;
00007 
00008 /**
00009  * Utility functions that do not really belong to any class in particular.
00010  *
00011  * @version $Id: Utility.java,v 1.1.1.1 2002/01/24 03:41:38 pserver Exp $
00012  * @author  <A HREF="http://www.inf.fu-berlin.de/~dahm">M. Dahm</A>
00013  */
00014 public abstract class Utility {
00015   private static int consumed_chars; /* How many chars have been consumed
00016                       * during parsing in signatureToString().
00017                       * Read by methodSignatureToString().
00018                       * Set by side effect,but only internally.
00019                       */
00020   private static boolean wide=false; /* The `WIDE' instruction is used in the
00021                       * byte code to allow 16-bit wide indices
00022                       * for local variables. This opcode
00023                       * precedes an `ILOAD', e.g.. The opcode
00024                       * immediately following takes an extra
00025                       * byte which is combined with the
00026                       * following byte to form a
00027                       * 16-bit value.
00028                       */
00029   /**
00030    * Convert bit field of flags into string such as `static final'.
00031    *
00032    * @param  access_flags Access flags
00033    * @return String representation of flags
00034    */
00035   public static final String accessToString(int access_flags) {
00036     return accessToString(access_flags, false);
00037   }  
00038   /**
00039    * Convert bit field of flags into string such as `static final'.
00040    *
00041    * Special case: Classes compiled with new compilers and with the
00042    * `ACC_SUPER' flag would be said to be "synchronized". This is
00043    * because SUN used the same value for the flags `ACC_SUPER' and
00044    * `ACC_SYNCHRONIZED'. 
00045    *
00046    * @param  access_flags Access flags
00047    * @param  for_class access flags are for class qualifiers ?
00048    * @return String representation of flags
00049    */
00050   public static final String accessToString(int access_flags, 
00051                         boolean for_class)
00052   {
00053     StringBuffer buf = new StringBuffer();
00054 
00055     int p = 0;
00056     for(int i=0; p < Constants.MAX_ACC_FLAG; i++) { // Loop through known flags
00057       p = pow2(i);
00058 
00059       if((access_flags & p) != 0) {
00060     /* Special case: Classes compiled with new compilers and with the
00061      * `ACC_SUPER' flag would be said to be "synchronized". This is
00062      * because SUN used the same value for the flags `ACC_SUPER' and
00063      * `ACC_SYNCHRONIZED'.
00064      */
00065     if(for_class && ((p == Constants.ACC_SUPER) || (p == Constants.ACC_INTERFACE)))
00066       continue;     
00067 
00068     buf.append(Constants.ACCESS_NAMES[i] + " ");
00069       }
00070     }
00071 
00072     return buf.toString().trim();
00073   }  
00074   /**
00075    * Convert (signed) byte to (unsigned) short value, i.e., all negative
00076    * values become positive.
00077    */
00078   private static final short byteToShort(byte b) {
00079     return (b < 0)? (short)(256 + b) : (short)b;
00080   }  
00081   /**
00082    * @return "class" or "interface", depending on the ACC_INTERFACE flag
00083    */
00084   public static final String classOrInterface(int access_flags) {
00085     return ((access_flags & Constants.ACC_INTERFACE) != 0)? "interface" : "class";
00086   }  
00087   /**
00088    * @return `flag' with bit `i' set to 0
00089    */
00090   public static final int clearBit(int flag, int i) {
00091     int bit = pow2(i); 
00092     return (flag & bit) == 0? flag : flag ^ bit; 
00093   }  
00094   public static final String codeToString(byte[] code, 
00095                       ConstantPool constant_pool, 
00096                       int index, int length) {
00097     return codeToString(code, constant_pool, index, length, true);
00098   }  
00099   /**
00100    * Disassemble a byte array of JVM byte codes starting from code line 
00101    * `index' and return the dissambled string representation. Decode only
00102    * `num' opcodes (including their operands), use -1 if you want to
00103    * decompile everything.
00104    *
00105    * @param  code byte code array
00106    * @param  constant_pool Array of constants
00107    * @param  index offset in `code' array
00108    * <EM>(number of opcodes, not bytes!)</EM>
00109    * @param  length number of opcodes to decompile, -1 for all
00110    * @param  verbose be verbose, e.g. print constant pool index
00111    * @return String representation of byte codes
00112    */
00113   public static final String codeToString(byte[] code, 
00114                       ConstantPool constant_pool, 
00115                       int index, int length, boolean verbose)
00116   {
00117     StringBuffer buf    = new StringBuffer(code.length * 20); // Should be sufficient
00118     ByteSequence stream = new ByteSequence(code);
00119 
00120     try {
00121       for(int i=0; i < index; i++) // Skip `index' lines of code
00122     codeToString(stream, constant_pool, verbose);
00123 
00124       for(int i=0; stream.available() > 0; i++) {
00125     if((length < 0) || (i < length)) {
00126       String indices = fillup(stream.getIndex() + ":", 6, true, ' ');
00127       buf.append(indices + codeToString(stream, constant_pool, verbose) + '\n');
00128     }
00129       }
00130     } catch(IOException e) {
00131       System.out.println(buf.toString());
00132       e.printStackTrace();
00133       throw new ClassFormatError("Byte code error: " + e);
00134     }
00135 
00136     return buf.toString();
00137   }  
00138   public static final String codeToString(ByteSequence bytes, ConstantPool constant_pool)
00139     throws IOException
00140   {
00141     return codeToString(bytes, constant_pool, true);
00142   }  
00143   /**
00144    * Disassemble a stream of byte codes and return the
00145    * string representation.
00146    *
00147    * @param  stream data input stream
00148    * @param  constant_pool Array of constants
00149    * @param  verbose be verbose, e.g. print constant pool index
00150    * @return String representation of byte code
00151    */
00152   public static final String codeToString(ByteSequence bytes,
00153                       ConstantPool constant_pool, boolean verbose)
00154        throws IOException
00155   {
00156     short        opcode = (short)bytes.readUnsignedByte();
00157     int          default_offset=0, low, high, npairs;
00158     int          index, vindex, constant;
00159     int[]        match, jump_table;
00160     int          no_pad_bytes=0, offset;
00161     StringBuffer buf = new StringBuffer(Constants.OPCODE_NAMES[opcode]);
00162 
00163     /* Special case: Skip (0-3) padding bytes, i.e., the
00164      * following bytes are 4-byte-aligned
00165      */
00166     if((opcode == Constants.TABLESWITCH) || (opcode == Constants.LOOKUPSWITCH)) {
00167       int remainder = bytes.getIndex() % 4;
00168       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
00169 
00170       for(int i=0; i < no_pad_bytes; i++) {
00171     byte b;
00172 
00173     if((b=bytes.readByte()) != 0)
00174       System.err.println("Ooops. Padding byte != 0 " + b);
00175       }
00176 
00177       // Both cases have a field default_offset in common
00178       default_offset = bytes.readInt();
00179     }
00180 
00181     switch(opcode) {
00182       /* Table switch has variable length arguments.
00183        */
00184     case Constants.TABLESWITCH:
00185       low  = bytes.readInt();
00186       high = bytes.readInt();
00187 
00188       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
00189       default_offset += offset;
00190 
00191       buf.append("\tdefault = " + default_offset + ", low = " + low + 
00192          ", high = " + high + "(");
00193 
00194       jump_table = new int[high - low + 1];
00195       for(int i=0; i < jump_table.length; i++) {
00196     jump_table[i] = offset + bytes.readInt();
00197     buf.append(jump_table[i]);
00198 
00199     if(i < jump_table.length - 1)
00200       buf.append(", ");
00201       }
00202       buf.append(")");
00203 
00204       break;
00205 
00206       /* Lookup switch has variable length arguments.
00207        */
00208     case Constants.LOOKUPSWITCH: {
00209 
00210       npairs = bytes.readInt();
00211       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
00212       
00213       match      = new int[npairs];
00214       jump_table = new int[npairs];
00215       default_offset += offset;
00216 
00217       buf.append("\tdefault = " + default_offset + ", npairs = " + npairs +
00218          " (");
00219 
00220       for(int i=0; i < npairs; i++) {
00221     match[i]      = bytes.readInt();
00222 
00223     jump_table[i] = offset + bytes.readInt();
00224 
00225     buf.append("(" + match[i] + ", " + jump_table[i] + ")");
00226 
00227     if(i < npairs - 1)
00228       buf.append(", ");
00229       }
00230       buf.append(")");
00231     }
00232     break;
00233 
00234     /* Two address bytes + offset from start of byte stream form the
00235      * jump target
00236      */
00237     case Constants.GOTO:      case Constants.IFEQ:      case Constants.IFGE:      case Constants.IFGT:
00238     case Constants.IFLE:      case Constants.IFLT:      case Constants.JSR: case Constants.IFNE:
00239     case Constants.IFNONNULL: case Constants.IFNULL:    case Constants.IF_ACMPEQ:
00240     case Constants.IF_ACMPNE: case Constants.IF_ICMPEQ: case Constants.IF_ICMPGE: case Constants.IF_ICMPGT:
00241     case Constants.IF_ICMPLE: case Constants.IF_ICMPLT: case Constants.IF_ICMPNE:
00242       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readShort()));
00243       break;
00244       
00245       /* 32-bit wide jumps
00246        */
00247     case Constants.GOTO_W: case Constants.JSR_W:
00248       buf.append("\t\t#" + ((bytes.getIndex() - 1) + bytes.readInt()));
00249       break;
00250 
00251       /* Index byte references local variable (register)
00252        */
00253     case Constants.ALOAD:  case Constants.ASTORE: case Constants.DLOAD:  case Constants.DSTORE: case Constants.FLOAD:
00254     case Constants.FSTORE: case Constants.ILOAD:  case Constants.ISTORE: case Constants.LLOAD:  case Constants.LSTORE:
00255     case Constants.RET: 
00256       if(wide) {
00257     vindex = bytes.readUnsignedShort();
00258     wide=false; // Clear flag
00259       }
00260       else
00261     vindex = bytes.readUnsignedByte();
00262 
00263       buf.append("\t\t%" + vindex);
00264       break;
00265 
00266       /*
00267        * Remember wide byte which is used to form a 16-bit address in the
00268        * following instruction. Relies on that the method is called again with
00269        * the following opcode.
00270        */
00271     case Constants.WIDE:
00272       wide      = true;
00273       buf.append("\t(wide)");
00274       break;
00275 
00276       /* Array of basic type.
00277        */
00278     case Constants.NEWARRAY:
00279       buf.append("\t\t<" + Constants.TYPE_NAMES[bytes.readByte()] + ">");
00280       break;
00281 
00282       /* Access object/class fields.
00283        */
00284     case Constants.GETFIELD: case Constants.GETSTATIC: case Constants.PUTFIELD: case Constants.PUTSTATIC:
00285       index = bytes.readUnsignedShort();
00286       buf.append("\t\t" +
00287          constant_pool.constantToString(index, Constants.CONSTANT_Fieldref) +
00288          (verbose? " (" + index + ")" : ""));
00289       break;
00290       
00291       /* Operands are references to classes in constant pool
00292        */
00293     case Constants.NEW:
00294     case Constants.CHECKCAST:
00295       buf.append("\t");
00296     case Constants.INSTANCEOF:
00297       index = bytes.readUnsignedShort();
00298       buf.append("\t<" + constant_pool.constantToString(index,
00299                             Constants.CONSTANT_Class) +
00300          ">" + (verbose? " (" + index + ")" : ""));
00301       break;
00302 
00303       /* Operands are references to methods in constant pool
00304        */
00305     case Constants.INVOKESPECIAL: case Constants.INVOKESTATIC: case Constants.INVOKEVIRTUAL:
00306       index = bytes.readUnsignedShort();
00307       buf.append("\t" + constant_pool.constantToString(index,
00308                                Constants.CONSTANT_Methodref) +
00309          (verbose? " (" + index + ")" : ""));
00310       break;
00311 
00312     case Constants.INVOKEINTERFACE:
00313       index = bytes.readUnsignedShort();
00314       int nargs = bytes.readUnsignedByte(); // historical, redundant
00315       buf.append("\t" + 
00316          constant_pool.constantToString(index,
00317                         Constants.CONSTANT_InterfaceMethodref) +
00318          (verbose? " (" + index + ")\t" : "") + nargs + "\t" + 
00319          bytes.readUnsignedByte()); // Last byte is a reserved space
00320       break;
00321     
00322       /* Operands are references to items in constant pool
00323        */
00324     case Constants.LDC_W: case Constants.LDC2_W:
00325       index = bytes.readUnsignedShort();
00326 
00327       buf.append("\t\t" + constant_pool.constantToString
00328          (index, constant_pool.getConstant(index).getTag()) +
00329          (verbose? " (" + index + ")" : ""));
00330       break;
00331 
00332     case Constants.LDC:
00333       index = bytes.readUnsignedByte();
00334 
00335       buf.append("\t\t" + 
00336          constant_pool.constantToString
00337          (index, constant_pool.getConstant(index).getTag()) +
00338          (verbose? " (" + index + ")" : ""));
00339       break;
00340     
00341       /* Array of references.
00342        */
00343     case Constants.ANEWARRAY:
00344       index = bytes.readUnsignedShort();
00345       
00346       buf.append("\t\t<" + compactClassName(constant_pool.getConstantString
00347                       (index, Constants.CONSTANT_Class), false) +
00348          ">" + (verbose? " (" + index + ")": ""));
00349       break;
00350     
00351       /* Multidimensional array of references.
00352        */
00353     case Constants.MULTIANEWARRAY: {
00354       index          = bytes.readUnsignedShort();
00355       int dimensions = bytes.readUnsignedByte();
00356 
00357       buf.append("\t<" + compactClassName(constant_pool.getConstantString
00358                       (index, Constants.CONSTANT_Class), false) +
00359          ">\t" + dimensions + (verbose? " (" + index + ")" : ""));
00360     }
00361     break;
00362 
00363     /* Increment local variable.
00364      */
00365     case Constants.IINC:
00366       if(wide) {
00367     vindex   = bytes.readUnsignedShort();
00368     constant = bytes.readShort();
00369     wide     = false;
00370       }
00371       else {
00372     vindex   = bytes.readUnsignedByte();
00373     constant = bytes.readByte();
00374       }
00375       buf.append("\t\t%" + vindex + "\t" + constant);
00376       break;
00377 
00378     default:
00379       if(Constants.NO_OF_OPERANDS[opcode] > 0) {
00380     for(int i=0; i < Constants.TYPE_OF_OPERANDS[opcode].length; i++) {
00381       buf.append("\t\t");
00382       switch(Constants.TYPE_OF_OPERANDS[opcode][i]) {
00383       case Constants.T_BYTE:  buf.append(bytes.readByte()); break;
00384       case Constants.T_SHORT: buf.append(bytes.readShort());       break;
00385       case Constants.T_INT:   buf.append(bytes.readInt());         break;
00386                           
00387       default: // Never reached
00388         System.err.println("Unreachable default case reached!");
00389         System.exit(-1);
00390       }
00391     }
00392       }
00393     }
00394 
00395     return buf.toString();
00396   }  
00397   /**
00398    * Shorten long class names, <em>java/lang/String</em> becomes 
00399    * <em>String</em>.
00400    *
00401    * @param str The long class name
00402    * @return Compacted class name
00403    */
00404   public static final String compactClassName(String str) {
00405     return compactClassName(str, true);
00406   }  
00407   /**
00408    * Shorten long class name <em>str</em>, i.e., chop off the <em>prefix</em>,
00409    * if the
00410    * class name starts with this string and the flag <em>chopit</em> is true.
00411    * Slashes <em>/</em> are converted to dots <em>.</em>.
00412    *
00413    * @param str The long class name
00414    * @param prefix The prefix the get rid off
00415    * @param chopit Flag that determines whether chopping is executed or not
00416    * @return Compacted class name
00417    */
00418   public static final String compactClassName(String str, 
00419                           String prefix,
00420                           boolean chopit)
00421   {
00422     int len = prefix.length();
00423 
00424     str = str.replace('/', '.'); // Is `/' on all systems, even DOS
00425 
00426     if(chopit) {
00427       // If string starts with `prefix' and contains no further dots
00428       if(str.startsWith(prefix) &&
00429      (str.substring(len).indexOf('.') == -1))
00430     str = str.substring(len);
00431     }
00432     
00433     return str;
00434   }  
00435   /**
00436    * Shorten long class names, <em>java/lang/String</em> becomes 
00437    * <em>java.lang.String</em>,
00438    * e.g.. If <em>chopit</em> is <em>true</em> the prefix <em>java.lang</em>
00439    * is also removed.
00440    *
00441    * @param str The long class name
00442    * @param chopit Flag that determines whether chopping is executed or not
00443    * @return Compacted class name
00444    */
00445   public static final String compactClassName(String str, boolean chopit) {
00446     return compactClassName(str, "java.lang.", chopit);
00447   }  
00448   private static int countBrackets(String brackets) {
00449     char[]  chars = brackets.toCharArray();
00450     int     count = 0;
00451     boolean open  = false;
00452 
00453     for(int i=0; i<chars.length; i++) {
00454       switch(chars[i]) {
00455       case '[':
00456     if(open)
00457       throw new RuntimeException("Illegally nested brackets:" + brackets);
00458     open = true;
00459     break;
00460 
00461       case ']':
00462     if(!open)
00463       throw new RuntimeException("Illegally nested brackets:" + brackets);
00464     open = false;
00465     count++;
00466     break;
00467 
00468       default:
00469     // Don't care
00470       }
00471     }
00472 
00473     if(open)
00474       throw new RuntimeException("Illegally nested brackets:" + brackets);
00475 
00476     return count;
00477   }  
00478   static final boolean equals(byte[] a, byte[] b) {
00479     int size;
00480 
00481     if((size=a.length) != b.length)
00482       return false;
00483 
00484     for(int i=0; i < size; i++)
00485       if(a[i] != b[i])
00486     return false;
00487 
00488     return true;
00489   }  
00490   /**
00491    * Fillup char with up to length characters with char `fill' and justify it left or right.
00492    *
00493    * @param str string to format
00494    * @param length length of desired string
00495    * @param left_justify format left or right
00496    * @param fill fill character
00497    * @return formatted string
00498    */
00499   public static final String fillup(String str, int length, boolean left_justify, char fill) {
00500     int    len = length - str.length();
00501     char[] buf = new char[(len < 0)? 0 : len];
00502 
00503     for(int j=0; j < buf.length; j++)
00504       buf[j] = fill;
00505 
00506     if(left_justify)
00507       return str + new String(buf);    
00508     else
00509       return new String(buf) + str;
00510   }  
00511   /**
00512    * Return a string for an integer justified left or right and filled up with
00513    * `fill' characters if necessary.
00514    *
00515    * @param i integer to format
00516    * @param length length of desired string
00517    * @param left_justify format left or right
00518    * @param fill fill character
00519    * @return formatted int
00520    */
00521   public static final String format(int i, int length, boolean left_justify, char fill) {
00522     return fillup(Integer.toString(i), length, left_justify, fill);
00523   }  
00524   /** Parse Java type such as "char", or "java.lang.String[]" and return the
00525    * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively.
00526    *
00527    * @param  type Java type
00528    * @return byte code signature
00529    */
00530   public static String getSignature(String type) {
00531     StringBuffer buf   = new StringBuffer();
00532     char[]       chars = type.toCharArray();
00533     boolean char_found = false, delim = false;
00534     int     index      = -1;
00535 
00536   loop:
00537     for(int i=0; i<chars.length; i++) {
00538       switch(chars[i]) {
00539       case ' ': case '\t': case '\n': case '\r': case '\f':
00540     if(char_found)
00541       delim = true;
00542     break;
00543 
00544       case '[':
00545     if(!char_found)
00546       throw new RuntimeException("Illegal type: " + type);
00547 
00548     index = i;
00549     break loop;
00550 
00551       default:
00552     char_found = true;
00553     if(!delim)
00554       buf.append(chars[i]);
00555       }
00556     }
00557 
00558     int brackets = 0;
00559 
00560     if(index > 0)
00561       brackets = countBrackets(type.substring(index));
00562 
00563     type = buf.toString();
00564     buf.setLength(0);
00565 
00566     for(int i=0; i < brackets; i++)
00567       buf.append('[');
00568 
00569     boolean found = false;
00570 
00571     for(int i=Constants.T_BOOLEAN; (i <= Constants.T_LONG) && !found; i++) {
00572       if(Constants.TYPE_NAMES[i].equals(type)) {
00573     found = true;
00574     buf.append(Constants.SHORT_TYPE_NAMES[i]);
00575       }
00576     }
00577     
00578     if(!found) // Class name
00579       buf.append('L' + type.replace('.', '/') + ';');
00580 
00581     return buf.toString();
00582   }  
00583   private static final boolean is_digit(char ch) {
00584     return (ch >= '0') && (ch <= '9');
00585   }  
00586   private static final boolean is_space(char ch) {
00587     return (ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n');
00588   }  
00589   /**
00590    * @return true, if bit `i' in `flag' is set
00591    */
00592   public static final boolean isSet(int flag, int i) {
00593     return (flag & pow2(i)) != 0;
00594   }  
00595   /**
00596    * @param  signature    Method signature
00597    * @return Array of argument types
00598    * @throw  ClassFormatError  
00599    */
00600   public static final String[] methodSignatureArgumentTypes(String signature)
00601     throws ClassFormatError 
00602   {
00603     return methodSignatureArgumentTypes(signature, true);
00604   }  
00605   /**
00606    * @param  signature    Method signature
00607    * @param chopit Shorten class names ?
00608    * @return Array of argument types
00609    * @throw  ClassFormatError  
00610    */
00611   public static final String[] methodSignatureArgumentTypes(String signature,
00612                                 boolean chopit)
00613     throws ClassFormatError
00614   {
00615     Vector   vec = new Vector();
00616     int      index;
00617     String[] types;
00618 
00619     try { // Read all declarations between for `(' and `)'
00620       if(signature.charAt(0) != '(')
00621     throw new ClassFormatError("Invalid method signature: " + signature);
00622 
00623       index = 1; // current string position
00624 
00625       while(signature.charAt(index) != ')') {
00626     vec.addElement(signatureToString(signature.substring(index), chopit));
00627     index += consumed_chars; // update position
00628       }
00629     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00630       throw new ClassFormatError("Invalid method signature: " + signature);
00631     }
00632     
00633     types = new String[vec.size()];
00634     vec.copyInto(types);
00635     return types;
00636   }  
00637   /**
00638    * @param  signature    Method signature
00639    * @return return type of method
00640    * @throw  ClassFormatError  
00641    */
00642   public static final String methodSignatureReturnType(String signature)
00643        throws ClassFormatError 
00644   {
00645     return methodSignatureReturnType(signature, true);
00646   }  
00647   /**
00648    * @param  signature    Method signature
00649    * @param chopit Shorten class names ?
00650    * @return return type of method
00651    * @throw  ClassFormatError  
00652    */
00653   public static final String methodSignatureReturnType(String signature,
00654                                boolean chopit)
00655        throws ClassFormatError
00656   {
00657     int    index;
00658     String type;
00659 
00660     try {
00661       // Read return type after `)'
00662       index = signature.lastIndexOf(')') + 1; 
00663       type = signatureToString(signature.substring(index), chopit);
00664     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00665       throw new ClassFormatError("Invalid method signature: " + signature);
00666     }
00667 
00668     return type;
00669   }  
00670   /**
00671    * Converts method signature to string with all class names compacted.
00672    *
00673    * @param signature to convert
00674    * @param name of method
00675    * @param access flags of method
00676    * @return Human readable signature
00677    */
00678   public static final String methodSignatureToString(String signature,
00679                              String name,
00680                              String access) {
00681     return methodSignatureToString(signature, name, access, true);
00682   }  
00683   /**
00684    * A return­type signature represents the return value from a method.
00685    * It is a series of bytes in the following grammar:
00686    *
00687    * <return_signature> ::= <field_type> | V
00688    *
00689    * The character V indicates that the method returns no value. Otherwise, the
00690    * signature indicates the type of the return value.
00691    * An argument signature represents an argument passed to a method:
00692    *
00693    * <argument_signature> ::= <field_type>
00694    *
00695    * A method signature represents the arguments that the method expects, and
00696    * the value that it returns.
00697    * <method_signature> ::= (<arguments_signature>) <return_signature>
00698    * <arguments_signature>::= <argument_signature>*
00699    *
00700    * This method converts such a string into a Java type declaration like
00701    * `void main(String[])' and throws a `ClassFormatError' when the parsed 
00702    * type is invalid.
00703    *
00704    * @param  signature    Method signature
00705    * @param  name         Method name
00706    * @param  access       Method access rights
00707    * @return Java type declaration
00708    * @throw  ClassFormatError  
00709    */
00710   public static final String methodSignatureToString(String signature,
00711                              String name,
00712                              String access,
00713                              boolean chopit)
00714        throws ClassFormatError
00715   {
00716     StringBuffer buf = new StringBuffer("(");
00717     String       type;
00718     int          index;
00719 
00720     try { // Read all declarations between for `(' and `)'
00721       if(signature.charAt(0) != '(')
00722     throw new ClassFormatError("Invalid method signature: " + signature);
00723 
00724       index = 1; // current string position
00725 
00726       while(signature.charAt(index) != ')') {
00727     buf.append(signatureToString(signature.substring(index), chopit) +
00728            ", ");
00729     index += consumed_chars; // update position
00730       }
00731 
00732       index++; // update position
00733 
00734       // Read return type after `)'
00735       type = signatureToString(signature.substring(index), chopit);
00736 
00737     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00738       throw new ClassFormatError("Invalid method signature: " + signature);
00739     }
00740 
00741     if(buf.length() > 1)               // Tack off the extra ", "
00742       buf.setLength(buf.length() - 2);
00743 
00744     buf.append(")");
00745 
00746     return access + ((access.length() > 0)?" " : "") + // May be an empty string
00747       type + " " + name + buf.toString();
00748   }  
00749   /**
00750    * Converts string containing the method return and argument types 
00751    * to a byte code method signature.
00752    *
00753    * @param  ret Return type of method
00754    * @param  argv Types of method arguments
00755    * @return Byte code representation of method type
00756    */
00757   public final static String methodTypeToSignature(String ret, String[] argv)
00758     throws ClassFormatError
00759   {
00760     StringBuffer buf = new StringBuffer("(");
00761     String       str;
00762 
00763     if(argv != null)
00764       for(int i=0; i < argv.length; i++) {
00765     str = typeToSignature(argv[i]);
00766 
00767     if(str.endsWith("V")) // void can't be a method argument
00768       throw new ClassFormatError("Invalid type: " + argv[i]);
00769 
00770     buf.append(str);
00771       }
00772 
00773     str = typeToSignature(ret);
00774 
00775     buf.append(")" + str);
00776 
00777     return buf.toString();
00778   }  
00779   // Guess what this does
00780   private static final int pow2(int n) {
00781     return 1 << n;
00782   }  
00783   public static final String printArray(Object[] obj) {
00784     return printArray(obj, true);
00785   }  
00786   public static final String printArray(Object[] obj, boolean braces) {
00787     if(obj == null)
00788       return null;
00789 
00790     StringBuffer buf = new StringBuffer();
00791     if(braces)
00792       buf.append('{');
00793 
00794     for(int i=0; i < obj.length; i++) {
00795       if(obj[i] != null)
00796     buf.append(obj[i].toString());
00797       else
00798     buf.append("null");
00799 
00800       if(i < obj.length - 1)
00801     buf.append(", ");
00802     }
00803 
00804     if(braces)
00805       buf.append('}');
00806 
00807     return buf.toString();
00808   }  
00809   public static final void printArray(PrintStream out, Object[] obj) {
00810     out.println(printArray(obj, true));
00811   }  
00812   public static final void printArray(PrintWriter out, Object[] obj) {
00813     out.println(printArray(obj, true));
00814   }  
00815   /**
00816    * Replace all occurences of <em>old</em> in <em>str</em> with <em>new</em>.
00817    *
00818    * @param str String to permute
00819    * @param old String to be replaced
00820    * @param new Replacement string
00821    * @return new String object
00822    */
00823   public static final String replace(String str, String old, String new_) {
00824     int          index, old_index;
00825     StringBuffer buf = new StringBuffer();
00826 
00827     try {
00828       if((index = str.indexOf(old)) != -1) { // `old' found in str
00829     old_index = 0;                       // String start offset
00830       
00831     // While we have something to replace
00832     while((index = str.indexOf(old, old_index)) != -1) {
00833       buf.append(str.substring(old_index, index)); // append prefix
00834       buf.append(new_);                            // append replacement
00835           
00836       old_index = index + old.length(); // Skip `old'.length chars
00837     }
00838 
00839     buf.append(str.substring(old_index)); // append rest of string
00840     str = buf.toString();   
00841       }
00842     } catch(StringIndexOutOfBoundsException e) { // Should not occur
00843       System.err.println(e);
00844     }
00845 
00846     return str;
00847   }  
00848   /** Map opcode names to opcode numbers. E.g., return Constants.ALOAD for "aload"
00849    */
00850   public static short searchOpcode(String name) {
00851     name = name.toLowerCase();
00852 
00853     for(short i=0; i < Constants.OPCODE_NAMES.length; i++)
00854       if(Constants.OPCODE_NAMES[i].equals(name))
00855     return i;
00856     
00857     return -1;
00858   }  
00859   /**
00860    * @return `flag' with bit `i' set to 1
00861    */
00862   public static final int setBit(int flag, int i) {
00863     return flag | pow2(i); 
00864   }  
00865   /**
00866    * Converts signature to string with all class names compacted.
00867    *
00868    * @param signature to convert
00869    * @return Human readable signature
00870    */
00871   public static final String signatureToString(String signature) {
00872     return signatureToString(signature, true);
00873   }  
00874   /**
00875    * The field signature represents the value of an argument to a function or 
00876    * the value of a variable. It is a series of bytes generated by the 
00877    * following grammar:
00878    *
00879    * <PRE>
00880    * <field_signature> ::= <field_type>
00881    * <field_type>      ::= <base_type>|<object_type>|<array_type>
00882    * <base_type>       ::= B|C|D|F|I|J|S|Z
00883    * <object_type>     ::= L<fullclassname>;
00884    * <array_type>      ::= [<field_type>
00885    *
00886    * The meaning of the base types is as follows:
00887    * B byte signed byte
00888    * C char character
00889    * D double double precision IEEE float
00890    * F float single precision IEEE float
00891    * I int integer
00892    * J long long integer
00893    * L<fullclassname>; ... an object of the given class
00894    * S short signed short
00895    * Z boolean true or false
00896    * [<field sig> ... array
00897    * </PRE>
00898    *
00899    * This method converts this string into a Java type declaration such as
00900    * `String[]' and throws a `ClassFormatError' when the parsed type is 
00901    * invalid.
00902    *
00903    * @param  signature  Class signature
00904    * @param chopit Flag that determines whether chopping is executed or not
00905    * @return Java type declaration
00906    * @throws ClassFormatError
00907    */
00908   public static final String signatureToString(String signature,
00909                            boolean chopit)
00910   {
00911     consumed_chars = 1; // This is the default, read just one char like `B'
00912 
00913     try {
00914       switch(signature.charAt(0)) {
00915       case 'B' : return "byte";
00916       case 'C' : return "char";
00917       case 'D' : return "double";
00918       case 'F' : return "float";
00919       case 'I' : return "int";
00920       case 'J' : return "long";
00921 
00922       case 'L' : { // Full class name
00923     int    index = signature.indexOf(';'); // Look for closing `;'
00924 
00925     if(index < 0)
00926       throw new ClassFormatError("Invalid signature: " + signature);
00927     
00928     consumed_chars = index + 1; // "Lblabla;" `L' and `;' are removed
00929 
00930     return compactClassName(signature.substring(1, index), chopit);
00931       }
00932 
00933       case 'S' : return "short";
00934       case 'Z' : return "boolean";
00935 
00936       case '[' : { // Array declaration
00937     int          n;
00938     StringBuffer buf, brackets;
00939     String       type;
00940     char         ch;
00941     int          consumed_chars; // Shadows global var
00942 
00943     brackets = new StringBuffer(); // Accumulate []'s
00944 
00945     // Count opening brackets and look for optional size argument
00946     for(n=0; signature.charAt(n) == '['; n++)
00947       brackets.append("[]");
00948 
00949     consumed_chars = n; // Remember value
00950 
00951     // The rest of the string denotes a `<field_type>'
00952     type = signatureToString(signature.substring(n), chopit);
00953     
00954     Utility.consumed_chars += consumed_chars;
00955     return type + brackets.toString();
00956       }
00957 
00958       case 'V' : return "void";
00959 
00960       default  : throw new ClassFormatError("Invalid signature: `" +
00961                         signature + "'");
00962       }
00963     } catch(StringIndexOutOfBoundsException e) { // Should never occur
00964       throw new ClassFormatError("Invalid signature: " + e + ":" + signature);
00965     }
00966   }  
00967   /** Convert bytes into hexidecimal string
00968    *
00969    * @return bytes as hexidecimal string, e.g. 00 FA 12 ...
00970    */
00971   public static final String toHexString(byte[] bytes) {
00972     StringBuffer buf = new StringBuffer();
00973 
00974     for(int i=0; i < bytes.length; i++) {
00975       short  b   = byteToShort(bytes[i]);
00976       String hex = Integer.toString(b, 0x10);
00977 
00978       if(b < 0x10) // just one digit, prepend '0'
00979     buf.append('0');
00980 
00981       buf.append(hex);
00982 
00983       if(i < bytes.length - 1)
00984     buf.append(' ');
00985     }
00986 
00987     return buf.toString();
00988   }  
00989   /**
00990    * Return type of method signature as a byte value as defined in <em>Constants</em>
00991    *
00992    * @param  signature in format described above
00993    * @return type of method signature
00994    * @see    Constants
00995    */
00996   public static final byte typeOfMethodSignature(String signature)
00997     throws ClassFormatError
00998   {
00999     int index;
01000 
01001     try {
01002       if(signature.charAt(0) != '(')
01003     throw new ClassFormatError("Invalid method signature: " + signature);
01004 
01005       index = signature.lastIndexOf(')') + 1;
01006       return typeOfSignature(signature.substring(index));
01007     } catch(StringIndexOutOfBoundsException e) {
01008       throw new ClassFormatError("Invalid method signature: " + signature);
01009     }
01010   }  
01011   /**
01012    * Return type of signature as a byte value as defined in <em>Constants</em>
01013    *
01014    * @param  signature in format described above
01015    * @return type of signature
01016    * @see    Constants
01017    */
01018   public static final byte typeOfSignature(String signature)
01019     throws ClassFormatError
01020   {
01021     try {
01022       switch(signature.charAt(0)) {
01023       case 'B' : return Constants.T_BYTE;
01024       case 'C' : return Constants.T_CHAR;
01025       case 'D' : return Constants.T_DOUBLE;
01026       case 'F' : return Constants.T_FLOAT;
01027       case 'I' : return Constants.T_INT;
01028       case 'J' : return Constants.T_LONG;
01029       case 'L' : return Constants.T_REFERENCE;
01030       case '[' : return Constants.T_ARRAY;
01031       case 'V' : return Constants.T_VOID;
01032       case 'Z' : return Constants.T_BOOLEAN;
01033       case 'S' : return Constants.T_SHORT;
01034       default:  
01035     throw new ClassFormatError("Invalid method signature: " + signature);
01036       }
01037     } catch(StringIndexOutOfBoundsException e) {
01038       throw new ClassFormatError("Invalid method signature: " + signature);
01039     }
01040   }  
01041   /**
01042    * Gets Java conformant type like `java.lang.String[]' and returns a string containing
01043    * the type in byte code format, i.e., String[] becomes [Ljava/lang/String;
01044    *
01045    * @param  str Type string like int[][]
01046    * @return Byte code representation of type like [[I
01047    * @deprecated replaced by getSignature
01048    */
01049   public final static String typeToSignature(String str) {
01050     return getSignature(str);
01051     /*int    index = str.indexOf('[');
01052     String type, array=null, code=null;
01053 
01054     try {
01055       if(index > -1) { // Is an array?
01056     type  = str.substring(0, index);
01057     array = str.substring(index);
01058       }
01059       else
01060     type = str;
01061 
01062       if(array == null) // Not an array
01063     array = "";
01064       else {
01065     StringBuffer buf = new StringBuffer();
01066     char ch, lastchar='X';
01067 
01068     for(int i=0; i < array.length(); i++) {
01069       ch = array.charAt(i);
01070 
01071       if(ch == '[') 
01072         buf.append('[');
01073       else if((ch == ']') || is_space(ch)) // Ignore
01074         ;
01075       else if(is_digit(ch)) {
01076         if((lastchar == '[') || is_digit(lastchar)) // Then it's OK
01077           buf.append(ch);
01078         else
01079           throw new ClassFormatError("Invalid type: " + str);
01080       }
01081       else
01082         throw new ClassFormatError("Invalid type: " + str);
01083 
01084       lastchar = ch;
01085     }
01086 
01087     array = buf.toString();
01088       }
01089     } catch(StringIndexOutOfBoundsException e) {
01090       throw new ClassFormatError("Invalid type: " + str);
01091     }
01092 
01093     int i;
01094     for(i=Constants.T_BOOLEAN; i <= Constants.T_VOID; i++) {
01095       if(type.equals(Constants.TYPE_NAMES[i])) {
01096     code = Constants.SHORT_TYPE_NAMES[i];
01097     break;
01098       }
01099     }
01100 
01101     if(i == Constants.T_VOID) {
01102       if(array.startsWith("[")) // Array of void !?
01103     throw new ClassFormatError("Invalid type: " + str);
01104     } else if(i > Constants.T_VOID) // Interpret as class name
01105       code = "L" + type.replace('.', '/') + ";";
01106 
01107       return array + code;*/
01108   }  
01109 }

Generated at Thu Feb 7 06:59:06 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001