Mega Code Archive

 
Categories / Java / Reflection
 

Manipulate Java class files in strange and mysterious ways

/*  * ClassFile.java Chuck McManis  *  * Copyright (c) 1996 Chuck McManis, All Rights Reserved.  *  * Permission to use, copy, modify, and distribute this software  * and its documentation for NON-COMMERCIAL purposes and without  * fee is hereby granted provided that this copyright notice  * appears in all copies.  *  * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY  * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  */ import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; /**  * This class is used to manipulate Java class files in strange and  * mysterious ways.  *  * Usage it typically to feed it an array of bytes that are a class  * file, manipulate the class, then convert the class back into bytes,  * and feed the final result to <TT>defineClass()</TT>.  *  * @version   1.6, 19 Aug 1995  * @author  Chuck McManis  * @see   AttributeInfo  * @see   ConstantPoolInfo  * @see   MethodInfo  * @see   FieldInfo  */ public class ClassFile {     int             magic;     short           majorVersion;     short           minorVersion;     ConstantPoolInfo  constantPool[];     short           accessFlags;     ConstantPoolInfo  thisClass;     ConstantPoolInfo  superClass;     ConstantPoolInfo  interfaces[];     FieldInfo       fields[];     MethodInfo        methods[];     AttributeInfo     attributes[];     boolean           isValidClass = false;     public static final int ACC_PUBLIC    = 0x1;     public static final int ACC_PRIVATE   = 0x2;     public static final int ACC_PROTECTED   = 0x4;     public static final int ACC_STATIC    = 0x8;     public static final int ACC_FINAL     = 0x10;     public static final int ACC_SYNCHRONIZED  = 0x20;     public static final int ACC_THREADSAFE  = 0x40;     public static final int ACC_TRANSIENT   = 0x80;     public static final int ACC_NATIVE    = 0x100;     public static final int ACC_INTERFACE   = 0x200;     public static final int ACC_ABSTRACT  = 0x400;     public boolean debug = false;     public boolean dumpConstants = false;     /**      * Read a class from InputStream <i>in</i>.      */     public boolean read(InputStream in)   throws IOException {       DataInputStream di = new DataInputStream(in);       int count;       magic = di.readInt();       if (magic != (int) 0xCAFEBABE) {           return (false);       }       majorVersion = di.readShort();       minorVersion = di.readShort();       count = di.readShort();       constantPool = new ConstantPoolInfo[count];       if (debug)           System.out.println("read(): Read header...");       constantPool[0] = new ConstantPoolInfo();       for (int i = 1; i < constantPool.length; i++) {           constantPool[i] = new ConstantPoolInfo();           if (! constantPool[i].read(di)) {             return (false);           }           // These two types take up "two" spots in the table           if ((constantPool[i].type == ConstantPoolInfo.LONG) ||             (constantPool[i].type == ConstantPoolInfo.DOUBLE))             i++;       }       /*        * Update pointers in the constant table. This turns the        * table into a real datastructure.        *        * TODO: Have it verify that the right arguments are present        */       for (int i = 1; i < constantPool.length; i++) {           if (constantPool[i] == null)             continue;           if (constantPool[i].index1 > 0)             constantPool[i].arg1 = constantPool[constantPool[i].index1];           if (constantPool[i].index2 > 0)             constantPool[i].arg2 = constantPool[constantPool[i].index2];       }       if (dumpConstants) {           for (int i = 1; i < constantPool.length; i++) {             System.out.println("C"+i+" - "+constantPool[i]);           }         }       accessFlags = di.readShort();       thisClass = constantPool[di.readShort()];       superClass = constantPool[di.readShort()];       if (debug)           System.out.println("read(): Read class info...");       /*        * Identify all of the interfaces implemented by this class        */       count = di.readShort();       if (count != 0) {           if (debug)               System.out.println("Class implements "+count+" interfaces.");           interfaces = new ConstantPoolInfo[count];           for (int i = 0; i < count; i++) {             int iindex = di.readShort();             if ((iindex < 1) || (iindex > constantPool.length - 1))                 return (false);             interfaces[i] = constantPool[iindex];             if (debug)                 System.out.println("I"+i+": "+interfaces[i]);           }       }       if (debug)           System.out.println("read(): Read interface info...");       /*        * Identify all fields in this class.        */       count = di.readShort();       if (debug)           System.out.println("This class has "+count+" fields.");       if (count != 0) {           fields = new FieldInfo[count];           for (int i = 0; i < count; i++) {             fields[i] = new FieldInfo();             if (! fields[i].read(di, constantPool)) {                return (false);             }             if (debug)                 System.out.println("F"+i+": "+               fields[i].toString(constantPool));           }       }       if (debug)           System.out.println("read(): Read field info...");       /*          * Identify all the methods in this class.        */       count = di.readShort();       if (count != 0) {           methods = new MethodInfo[count];           for (int i = 0; i < count; i++) {             methods[i] = new MethodInfo();             if (! methods[i].read(di, constantPool)) {                 return (false);             }             if (debug)                 System.out.println("M"+i+": "+methods[i].toString());           }       }       if (debug)           System.out.println("read(): Read method info...");       /*        * Identify all of the attributes in this class        */       count = di.readShort();       if (count != 0) {           attributes = new AttributeInfo[count];           for (int i = 0; i < count; i++) {             attributes[i] = new AttributeInfo();             if (! attributes[i].read(di, constantPool)) {                 return (false);             }           }       }       if (debug) {           System.out.println("read(): Read attribute info...");           System.out.println("done.");       }       isValidClass = true;       return(true);     }     /**      * Write the class out as a stream of bytes to the output      * stream.      *      * Generally you will read a class file, manipulate      * it in some way, and then write it out again before passing      * it to <TT>defineClass</TT> in some class loader.      */     public void write(OutputStream out)   throws IOException, Exception {       DataOutputStream dos = new DataOutputStream(out);       if (! isValidClass) {           throw new Exception("ClassFile::write() - Invalid Class");       }       dos.writeInt(magic);       dos.writeShort(majorVersion);       dos.writeShort(minorVersion);       dos.writeShort(constantPool.length);       for (int i = 1; i < constantPool.length; i++) {           if (constantPool[i] != null)               constantPool[i].write(dos, constantPool);       }       dos.writeShort(accessFlags);       dos.writeShort(ConstantPoolInfo.indexOf(thisClass, constantPool));       dos.writeShort(ConstantPoolInfo.indexOf(superClass, constantPool));       if (interfaces == null) {           dos.writeShort(0);       } else {           dos.writeShort(interfaces.length);           for (int i = 0; i < interfaces.length; i++) {               dos.writeShort(ConstantPoolInfo.indexOf(interfaces[i],               constantPool));           }       }       if (fields == null) {           dos.writeShort(0);       } else {           dos.writeShort(fields.length);           for (int i = 0; i < fields.length; i++) {               fields[i].write(dos, constantPool);           }       }       if (methods == null) {           dos.writeShort(0);       } else {           dos.writeShort(methods.length);           for (int i = 0; i < methods.length; i++) {               methods[i].write(dos, constantPool);           }       }       if (attributes == null) {           dos.writeShort(0);       } else {           dos.writeShort(attributes.length);           for (int i = 0; i < attributes.length; i++) {               attributes[i].write(dos, constantPool);           }       }     }     /**      * Returns a string that represents what the access flags      * are set for. So 0x14 returns "public final "      */     public static String accessString(short flags) {       StringBuffer x = new StringBuffer();       if ((flags & ACC_PUBLIC) != 0) {           x.append("public ");       }       if ((flags & ACC_PRIVATE) != 0) {           x.append("private ");       }       if ((flags & ACC_PROTECTED) != 0) {           x.append("protected ");       }       if ((flags & ACC_STATIC) != 0) {           x.append("static ");       }       if ((flags & ACC_FINAL) != 0) {           x.append("final ");       }       if ((flags & ACC_SYNCHRONIZED) != 0) {           x.append("synchronized ");       }       if ((flags & ACC_THREADSAFE) != 0) {           x.append("threadsafe ");       }       if ((flags & ACC_TRANSIENT) != 0) {           x.append("transient ");       }       if ((flags & ACC_NATIVE) != 0) {           x.append("native ");       }       if ((flags & ACC_INTERFACE) != 0) {           x.append("interface ");       }       if ((flags & ACC_ABSTRACT) != 0) {           x.append("abstract ");       }       return (x.toString());     }     /**      * Takes a type signature and a string representing a variable name      * and returns a declaration for that variable name.      *      * For example, passing this the strings "[B" and "myArray" will      * return the string "byte myArray[]"      */     public static String typeString(String typeString, String varName) {       int isArray = 0;       int ndx = 0;       StringBuffer x = new StringBuffer();       while (typeString.charAt(ndx) == '[') {           isArray++;           ndx++;       }       switch (typeString.charAt(ndx)) {           case 'B' :             x.append("byte ");             break;           case 'C' :             x.append("char ");             break;           case 'D' :             x.append("double ");             break;           case 'F' :             x.append("float ");             break;           case 'I' :             x.append("int ");             break;           case 'J' :             x.append("long ");             break;           case 'L' :             for (int i = ndx+1; i < typeString.indexOf(';'); i++) {                 if (typeString.charAt(i) != '/')                   x.append(typeString.charAt(i));                 else               x.append('.');             }             x.append(" ");             break;           case 'V':             x.append("void ");             break;           case 'S' :             x.append("short ");             break;           case 'Z' :             x.append("boolean ");             break;       }       x.append(varName);       while (isArray > 0) {           x.append("[]");           isArray--;       }       return (x.toString());     }     /**      * Returns the next signature from a string of concatenated signatures.      * For example if the signature was "[BII", this method would return      * "II"      */     public static String nextSig(String sig) {       int ndx = 0;       String  x;       while (sig.charAt(ndx) == '[')           ndx++;       if (sig.charAt(ndx) == 'L') {           while (sig.charAt(ndx) != ';')             ndx++;       }       ndx++;       x =  (sig.substring(ndx));       return (x);     }     /**      * Print the name of a class in "canonical form"      */     private String printClassName(String s) {       StringBuffer x;       if (s.charAt(0) == '[') {           return(typeString(s, ""));       }       x = new StringBuffer();       for (int j = 0; j < s.length(); j++) {           if (s.charAt(j) == '/')             x.append('.');           else             x.append(s.charAt(j));       }       return (x.toString());     }     public String getClassName() {       return printClassName(thisClass.arg1.strValue);     }     /**      * The boring version of display().      */     public String toString() {       return("Class File (Version "+majorVersion+"."+minorVersion+           ") for class "+thisClass.arg1);     }     /**      * Write out a text version of this class.      */     public void display(PrintStream ps)   throws Exception {       int i;       String myClassName;       String mySuperClassName;       String packageName = null;       if (! isValidClass) {           ps.println("Not a valid class");       }       myClassName = printClassName(thisClass.arg1.strValue);       mySuperClassName = printClassName(superClass.arg1.strValue);       if (myClassName.indexOf('.') > 0) {           packageName =             myClassName.substring(0, myClassName.lastIndexOf('.'));           myClassName = myClassName.substring(myClassName.lastIndexOf('.')+1);           ps.println("package "+packageName+"\n");       }       for (i = 1; i < constantPool.length; i++) {           if (constantPool[i] == null)             continue;           if ((constantPool[i] == thisClass) ||             (constantPool[i] == superClass))             continue;           if (constantPool[i].type == ConstantPoolInfo.CLASS) {             String s = constantPool[i].arg1.strValue;                 if (s.charAt(0) == '[')                     continue;             s = printClassName(constantPool[i].arg1.strValue);             if ((packageName != null) && (s.startsWith(packageName)))                 continue;             ps.println("import "+printClassName(s)+";");           }       }       ps.println();       ps.println("/*");       DataInputStream dis;       ConstantPoolInfo cpi;       if (attributes != null) {           ps.println(" * This class has "+attributes.length+           " optional class attributes.");           ps.println(" * These attributes are: ");           for (i = 0; i < attributes.length; i++) {               String attrName = attributes[i].name.strValue;               dis = new DataInputStream(new ByteArrayInputStream(attributes[i].data));               ps.println(" * Attribute "+(i+1)+" is of type "+attributes[i].name);               if (attrName.compareTo("SourceFile") == 0) {                 cpi = null;                 try {                     cpi = constantPool[dis.readShort()];                 } catch (IOException e) { }                 ps.println(" *  SourceFile : "+cpi);               } else {                 ps.println(" *  TYPE ("+attrName+")");               }             }       } else {           ps.println(" * This class has NO optional class attributes.");       }       ps.println(" */\n");       ps.print(accessString(accessFlags)+"class "+myClassName+" extends "+         mySuperClassName);       if (interfaces != null) {           ps.print(" implements ");           for (i = 0; i < interfaces.length - 1; i++) {             ps.print(interfaces[i].arg1.strValue+", ");           }           ps.print(interfaces[interfaces.length-1].arg1.strValue);       }       ps.println(" {\n");       if (fields != null) {           ps.println("/* Instance Variables */");           for (i = 0; i < fields.length; i++) {               ps.println("    "+fields[i].toString(constantPool)+";");           }       }       if (methods != null) {           ps.println("\n/* Methods */");           for (i = 0; i < methods.length; i++) {               ps.println("    "+methods[i].toString(myClassName));           }       }       ps.println("\n}");     }     public ConstantPoolInfo getConstantRef(short index) {       return (constantPool[index]);     }     /**      * Add a single constant pool item and return its index.      * If the item is already in the pool then the index of      * the <i>preexisting</i> item is returned. Thus you cannot      * assume that a pointer to your item will be useful.      */     public short addConstantPoolItem(ConstantPoolInfo item)   throws Exception {       ConstantPoolInfo newConstantPool[];       ConstantPoolInfo cp;       cp = item.inPool(constantPool);       if (cp != null)           return ConstantPoolInfo.indexOf(cp, constantPool);       newConstantPool = new ConstantPoolInfo[constantPool.length+1];       for (int i = 1; i < constantPool.length; i++) {           newConstantPool[i] = constantPool[i];       }       newConstantPool[constantPool.length] = item;       constantPool = newConstantPool;       return ConstantPoolInfo.indexOf(item, constantPool);     }     /**      * Add some items to the constant pool. This is used to add new      * items to the constant pool. The items references in arg1 and      * arg2 are expected to be valid pointers (if necessary). Pruning      * is done to prevent adding redundant items to the list and to      * preserve string space.      *      * The algorithm is simple, first identify pool items containing      * constants in the list of items to be added that are already      * in the constant pool. If any are found to already exist, change      * the pointers in the non-constant items to point to the ones in      * the pool rather than the ones in the list. Next check to see      * if any of the non-constant items are already in the pool and      * if so fix up the others in the list to point to the ones in      * the pool. Finally, add any items (there must be at least one)      * from the item list that aren't already in the pool, all of      * the pointers will already be fixed.      *      * NOTE: Since constants in the constant pool may be referenced      * <i>inside</i> the opaque portion of attributes the constant      * table cannot be re-ordered, only extended.      */     public void addConstantPoolItems(ConstantPoolInfo items[]) {       ConstantPoolInfo newArg;       ConstantPoolInfo newConstantPool[];       boolean delete[] = new boolean[items.length];       /* Step one, look for matching constants */       for (int j = 0; j < items.length; j++) {           if ( (items[j].type == ConstantPoolInfo.ASCIZ) ||                (items[j].type == ConstantPoolInfo.UNICODE) ||                (items[j].type == ConstantPoolInfo.INTEGER) ||                (items[j].type == ConstantPoolInfo.LONG) ||                (items[j].type == ConstantPoolInfo.FLOAT) ||                (items[j].type == ConstantPoolInfo.DOUBLE)) {         // Look for this item in the constant pool         delete[j] = false;         newArg = items[j].inPool(constantPool);         if (newArg != null) {             // replace the references in our list.             delete[j] = true; // mark it for deletion             for (int i = 0; i < items.length; i++) {               if (items[i].arg1 == items[j])                   items[i].arg1 = newArg;                   if (items[i].arg2 == items[j])                       items[i].arg2 = newArg;                 }             }           }       }       /* Step two : now match everything else */       for (int j = 0; j < items.length; j++) {           if ( (items[j].type == ConstantPoolInfo.CLASS) ||                (items[j].type == ConstantPoolInfo.FIELDREF) ||                (items[j].type == ConstantPoolInfo.METHODREF) ||                (items[j].type == ConstantPoolInfo.STRING) ||                (items[j].type == ConstantPoolInfo.INTERFACE) ||                (items[j].type == ConstantPoolInfo.NAMEANDTYPE)) {         // Look for this item in the constant pool         delete[j] = false;         newArg = items[j].inPool(constantPool);         if (newArg != null) {             // replace the references in our list.             delete[j] = true; // mark it for deletion             for (int i = 0; i < items.length; i++) {               if (items[i].arg1 == items[j])                   items[i].arg1 = newArg;                   if (items[i].arg2 == items[j])                       items[i].arg2 = newArg;                 }             }           }       }       /* Step three: Add the surviving items to the pool */       int count = 0;       for (int i = 0; i < items.length; i++) {           if (! delete[i])             count++;       }       // count == # of survivors       newConstantPool = new ConstantPoolInfo[constantPool.length + count];       for (int i = 1; i < constantPool.length; i++) {           newConstantPool[i] = constantPool[i];       }       // newConstantPool == existing constantPool       int ndx = 0;       for (int i = constantPool.length; i < newConstantPool.length; i++) {           while (delete[ndx])             ndx++;           newConstantPool[i] = items[ndx];           ndx++;       }       // newConstantPool == existing + new       constantPool = newConstantPool;       // all done.     }     /**      * Add a new optional class Attribute.      *      * Items is an array of constant pool items that are first added      * to the constant pool. At a minimum items[0] must be an ASCIZ      * item with the name of the attribute. If the body of the attribute      * references constant pool items these should be in the item list      * as well.      */     public void addAttribute(AttributeInfo newAttribute) {       if (attributes == null) {           attributes = new AttributeInfo[1];           attributes[0] = newAttribute;       } else {           AttributeInfo newAttrList[] = new AttributeInfo[1+attributes.length];           for (int i = 0; i < attributes.length; i++) {             newAttrList[i] = attributes[i];           }           newAttrList[attributes.length] = newAttribute;           attributes = newAttrList;       }     }     /**      * Return the attribute named 'name' from the class file.      */     public AttributeInfo getAttribute(String name) {       if (attributes == null)           return null;       for (int i = 0; i < attributes.length; i++) {           if (name.compareTo(attributes[i].name.toString()) == 0)         return attributes[i];       }       return (null);     }     /**      * Return a constant pool item from this class. (note does fixup      * of indexes to facilitate extracting nested or linked items.      */     public ConstantPoolInfo getConstantPoolItem(short index)   throws Exception {       ConstantPoolInfo cp;       if ((index <= 0) || (index > (constantPool.length - 1)))           return (null);       cp = constantPool[index];       if (cp.arg1 != null)           cp.index1 = ConstantPoolInfo.indexOf(cp.arg1, constantPool);       if (cp.arg2 != null)           cp.index2 = ConstantPoolInfo.indexOf(cp.arg2, constantPool);       return cp;     }     /* Examples of mysterious things you can do to a class file before      * writing it back out. These methods are not currently functional.      * (that would be too easy :-)      */     /**      * Map occurences of class <i>oldClass</i> to occurrences of      * class <i>newClass</i>. This method is used to retarget      * accesses to one class, seamlessly to another.      *      * The format for the class name is slash (/) separated so      * the class <tt>util.ClassFile</tt> would be represented as      * <tt>util/ClassFile</tt>      */     public void mapClass(String oldClass, String newClass) {       if (debug)           System.out.println("Mapping class name "+oldClass+" ==> "+           newClass+" for class "+thisClass.arg1);       for (int i = 0; i < constantPool.length; i++) {           if (constantPool[i].type == ConstantPoolInfo.CLASS) {             String cname = constantPool[i].arg1.strValue;             if (cname.compareTo(oldClass) == 0) {                 if (debug) {                     System.out.println("REPLACING "+cname+" with "+newClass);                 }                 constantPool[i].arg1.strValue = newClass;             }           }       }     }     /**      * Map occurences of package <i>oldPackage</i> to package      * <i>newPackage</i>.      *      * The format for the package name is slash (/) separated so      * the package <tt>java.util</tt> would be represented as      * <tt>java/util</tt>      */      public void mapPackage(String oldPackage, String newPackage) {       for (int i = 0; i < constantPool.length; i++) {           if (constantPool[i].type == ConstantPoolInfo.CLASS) {             String cname = constantPool[i].arg1.strValue;             if (cname.startsWith(oldPackage)) {                 constantPool[i].arg1.strValue = newPackage +               cname.substring(cname.lastIndexOf('/'));             }           }       }     }     /**      * Delete a named method from this class. This method is used      * to excise specific methods from the loaded class. The actual      * method code remains, however the method signature is deleted      * from the constant pool. If this method is called by a class      * the exception IncompatibleClassChangeException is generated      * by the runtime.      */     public void deleteMethod(String name, String signature) {       for (int i = 0; i < constantPool.length; i++) {           if (constantPool[i].type == ConstantPoolInfo.CLASS) {           }       }     } } /*  * @(#)ConstantPoolInfo.java  1.5 95/08/16 Chuck McManis  *  * Copyright (c) 1996 Chuck McManis, All Rights Reserved.  *  * Permission to use, copy, modify, and distribute this software  * and its documentation for NON-COMMERCIAL purposes and without  * fee is hereby granted provided that this copyright notice  * appears in all copies.  *  * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY  * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  */ /**  * This class defines an entry in the constant pool for a Java class.  * The class file is primarily composed of ConstantPool entries and  * manipulation is done by modifying those entries.  *  * @version   1.5, 16 Aug 1995  * @author  Chuck McManis  * @see   ClassFile  */  class ConstantPoolInfo{     int type;     // type of this item     String name;    // String for the type     ConstantPoolInfo  arg1; // index to first argument     ConstantPoolInfo  arg2; // index to second argument     short index1, index2;     String  strValue;     // ASCIZ String value     int   intValue;     long  longValue;     float floatValue;     double  doubleValue;     public static final int CLASS = 7;     public static final int FIELDREF = 9;     public static final int METHODREF = 10;     public static final int STRING = 8;     public static final int INTEGER = 3;     public static final int FLOAT = 4;     public static final int LONG = 5;     public static final int DOUBLE = 6;     public static final int INTERFACE = 11;     public static final int NAMEANDTYPE = 12;     public static final int ASCIZ = 1;     public static final int UNICODE = 2;     /**      * Construct a new ConstantPoolInfo object that is of type ASCIZ      */     public ConstantPoolInfo(String value) {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = ASCIZ;   strValue = value;     }     /**      * Construct a new ConstantPoolInfo object that is of type INTEGER      */     public ConstantPoolInfo(int value) {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = INTEGER;   intValue = value;     }     /**      * Construct a new ConstantPoolInfo object that is of type FLOAT      */     public ConstantPoolInfo(float value) {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = FLOAT;   floatValue = value;     }     /**      * Construct a new ConstantPoolInfo object that is of type LONG      */     public ConstantPoolInfo(long value) {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = LONG;   longValue = value;     }     /**      * Construct a new ConstantPoolInfo object that is of type DOUBLE      */     public ConstantPoolInfo(double value) {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = DOUBLE;   doubleValue = value;     }     /**      * Generic constructor      */     public ConstantPoolInfo() {   index1 = -1;   index2 = -1;   arg1 = null;   arg2 = null;   type = -1;     }     /**      * return the type of this constant pool item.      */     public int isType() {   return (type);     }     public boolean read(DataInputStream dis)   throws IOException {   int len;   char  c;   type = dis.readByte();   switch (type) {       case CLASS:     name = "Class";     index1 = dis.readShort();     index2 = -1;     break;       case FIELDREF:     name = "Field Reference";     index1 = dis.readShort();     index2 = dis.readShort();     break;       case METHODREF:     name = "Method Reference";     index1 = dis.readShort();     index2 = dis.readShort();     break;       case INTERFACE:     name = "Interface Method Reference";     index1 = dis.readShort();     index2 = dis.readShort();     break;       case NAMEANDTYPE:     name = "Name and Type";     index1 = dis.readShort();     index2 = dis.readShort();     break;       case STRING:     name = "String";     index1 = dis.readShort();     index2 = -1;     break;       case INTEGER:     name = "Integer";     intValue = dis.readInt();     break;       case FLOAT:     name = "Float";     floatValue = dis.readFloat();     break;       case LONG:     name = "Long";     longValue = dis.readLong();     break;       case DOUBLE:     name = "Double";     doubleValue = dis.readDouble();     break;       case ASCIZ:       case UNICODE:     if (type == ASCIZ)         name = "ASCIZ";     else         name = "UNICODE";     StringBuffer xxBuf = new StringBuffer();     len = dis.readShort();     while (len > 0) {         c = (char) (dis.readByte());         xxBuf.append(c);         len--;     }     strValue = xxBuf.toString();     break;       default:     System.out.println("Warning bad type.");   }   return (true);     }     public void write(DataOutputStream dos, ConstantPoolInfo pool[])   throws IOException, Exception {   dos.write(type);   switch (type) {       case CLASS:       case STRING:     dos.writeShort(indexOf(arg1, pool));     break;       case FIELDREF:       case METHODREF:       case INTERFACE:       case NAMEANDTYPE:     dos.writeShort(indexOf(arg1, pool));     dos.writeShort(indexOf(arg2, pool));     break;       case INTEGER:     dos.writeInt(intValue);     break;       case FLOAT:     dos.writeFloat(floatValue);     break;       case LONG:     dos.writeLong(longValue);     break;       case DOUBLE:     dos.writeDouble(doubleValue);     break;       case ASCIZ:       case UNICODE:     dos.writeShort(strValue.length());     dos.writeBytes(strValue);     break;       default:     throw new Exception("ConstantPoolInfo::write() - bad type.");   }     }     public String toString() {   StringBuffer s;   if (type == ASCIZ) {       return(strValue);   }   if (type == INTEGER) {       return("= "+intValue);   }   if (type == LONG) {       return("= "+longValue);   }   if (type == FLOAT) {       return("= "+floatValue);   }   if (type == DOUBLE) {       return("= "+doubleValue);   }   s = new StringBuffer();   s.append(name);   s.append(":");   if (arg1 != null)       s.append(arg1.toString());   else if (index1 != -1)       s.append("I1["+index1+"], ");   if (arg2 != null)       s.append(arg2.toString());   else if (index2 != -1)       s.append("I2["+index2+"], ");   return (s.toString());     }     public static short indexOf(ConstantPoolInfo item,           ConstantPoolInfo pool[])   throws Exception {   for (int i = 0; i < pool.length; i++) {       if (item == pool[i])     return (short) i;   }   throw new Exception("ConstantPoolInfo:: indexOf() - item not in pool.");     }     /**      * Returns true if these constant pool items are identical.      */     public boolean isEqual(ConstantPoolInfo cp) {   if (cp == null)       return false;   if (cp.type != type)       return (false);   switch (cp.type) {       case CLASS:       case STRING:     return (arg1 == cp.arg1);       case FIELDREF:       case METHODREF:       case INTERFACE:       case NAMEANDTYPE:     return ((arg1 == cp.arg1) && (arg2 == cp.arg2));       case INTEGER:     return (cp.intValue == intValue);       case FLOAT:     return (cp.floatValue == floatValue);       case LONG:     return (cp.longValue == longValue);       case DOUBLE:     return (cp.doubleValue == doubleValue);       case ASCIZ:       case UNICODE:     return (cp.strValue.compareTo(strValue) == 0);   }   return (false);     }     /**      * Returns the reference to the constant pool item that is      * already in pool, that matches this one.      */     public ConstantPoolInfo inPool(ConstantPoolInfo pool[]) {   for (int i = 1; i < pool.length; i++) {       if (isEqual(pool[i]))     return (pool[i]);   }   return null;     } }  /*   * @(#)MethodInfo.java 1.4 95/08/16 Chuck McManis   *   * Copyright (c) 1996 Chuck McManis, All Rights Reserved.   *   * Permission to use, copy, modify, and distribute this software   * and its documentation for NON-COMMERCIAL purposes and without   * fee is hereby granted provided that this copyright notice   * appears in all copies.   *   * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY   * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED   * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A   * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE   * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,   * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.   */  /**   * This class describes a Method as it is stored in the class file.   * The attribute associated with method is the code that actually implements   * the method. Since we don't need to manipulate the byte codes directly   * we leave them as an opaque chunk in the attributes[] array. References   * in the code are all references into the constant table so when we are   * modifing a class to use a different object we needn't get into the code   * level.   *   * @version  1.4, 16 Aug 1995   * @author Chuck McManis   * @see    ClassFile   */   class MethodInfo {      short    accessFlags;      ConstantPoolInfo   name;      ConstantPoolInfo   signature;      AttributeInfo  attributes[];      /**       * Read a method_info from the data stream.       */      public boolean read(DataInputStream di, ConstantPoolInfo pool[])   throws IOException {   int count;   accessFlags = di.readShort();   name = pool[di.readShort()];   signature = pool[di.readShort()];   count = di.readShort();   if (count != 0) {       attributes = new AttributeInfo[count];       for (int i = 0; i < count; i++) {     attributes[i] = new AttributeInfo(); // "code"     if (! attributes[i].read(di, pool)) {         return (false);     }       }   }   return (true);      }      /**       * Write out a method_info, do constant table fixups on the write.       */      public void write(DataOutputStream dos, ConstantPoolInfo pool[])   throws IOException, Exception {   dos.writeShort(accessFlags);   dos.writeShort(ConstantPoolInfo.indexOf(name, pool));   dos.writeShort(ConstantPoolInfo.indexOf(signature, pool));   if (attributes == null) {       dos.writeShort(0);   } else {       dos.writeShort(attributes.length);       for (int i = 0; i < attributes.length; i++)     attributes[i].write(dos, pool);   }      }      /**       * print out the method, much as you would see it in the source       * file. The string ClassName is substituted for &LTinit&GT when       * printing.       */      public String toString(String className) {   StringBuffer x = new StringBuffer();   boolean isArray = false;   String paramSig;   String returnSig;   int ndx = 0;   StringBuffer parameterList = new StringBuffer();   char  initialParameter = 'a';   StringBuffer varName = new StringBuffer();   String s = signature.strValue;   paramSig = s.substring(s.indexOf('(')+1, s.indexOf(')'));   returnSig = s.substring(s.indexOf(')')+1);   x.append(ClassFile.accessString(accessFlags));   /* catch constructors */   if ((className != null) && (name.toString().startsWith("<init>")))       parameterList.append(className);   else       parameterList.append(name.toString());   parameterList.append("(");   if ((paramSig.length() > 0) && paramSig.charAt(0) != 'V') {       while (paramSig.length() > 0) {     varName.setLength(0);     varName.append(initialParameter);     initialParameter++;     parameterList.append(       ClassFile.typeString(paramSig, varName.toString()));     paramSig = ClassFile.nextSig(paramSig);     if (paramSig.length() > 0)         parameterList.append(", ");       }          }   parameterList.append(")");   x.append(ClassFile.typeString(returnSig, parameterList.toString()));   x.append(";");   return (x.toString());      }      /**       * Generic toString method, init method is unchanged.       */      public String toString() {   return (toString((String)null));      }  }   /*    * @(#)AttributeInfo.java 1.4 95/08/16 Chuck McManis    *    * Copyright (c) 1996 Chuck McManis, All Rights Reserved.    *    * Permission to use, copy, modify, and distribute this software    * and its documentation for NON-COMMERCIAL purposes and without    * fee is hereby granted provided that this copyright notice    * appears in all copies.    *    * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY    * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED    * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A    * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE    * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,    * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.    */   /**    * This class defines the generic Attribute type for Java class files.    * It is a little bit smart in that for some Attributes it can display    * them intelligently if it also has access to the constant pool of the    * current class.    *    * @version   1.4, 16 Aug 1995    * @author  Chuck McManis    * @see   ClassFile    */   class AttributeInfo {       ConstantPoolInfo  name; // attribute name       byte    data[]; // attribute's contents       public AttributeInfo(ConstantPoolInfo newName, byte newData[]) {     name = name;     data = newData;       }       public AttributeInfo() {       }       public boolean read(DataInputStream di, ConstantPoolInfo pool[])     throws IOException {     int len;     name = pool[di.readShort()];     len = di.readInt();     data = new byte[len];     len  = di.read(data);     if (len != data.length)         return (false);     return (true);       }       public void write(DataOutputStream dos, ConstantPoolInfo pool[])     throws IOException, Exception {     dos.writeShort(ConstantPoolInfo.indexOf(name, pool));     dos.writeInt(data.length);     dos.write(data, 0, data.length);       }       short indexFromBytes(byte a[]) {     return (short)(((a[0] << 8) & (0xff << 8)) |              ((a[1] << 0) & (0xff << 0)));       }       public String toString(ConstantPoolInfo pool[]) {     StringBuffer x = new StringBuffer();     String type = name.toString();     ConstantPoolInfo item;     if (type.compareTo("ConstantValue") == 0) {         item = pool[indexFromBytes(data)];         return (item.toString());     } else if (type.compareTo("SourceFile") == 0) {         item = pool[indexFromBytes(data)];         return (item.toString());     } else {         x.append(type+"<"+data.length+" bytes>");     }     return (x.toString());       }       public String toBoolean(ConstantPoolInfo pool[]) {     ConstantPoolInfo item = pool[indexFromBytes(data)];     if (item.intValue == 0)         return ("= false");     return ("= true");       }       public String toString() {     return (name.toString()+" <"+data.length+" bytes>");       }   }   /*    * @(#)FieldInfo.java 1.3 95/08/16 Chuck McManis    *    * Copyright (c) 1996 Chuck McManis, All Rights Reserved.    *    * Permission to use, copy, modify, and distribute this software    * and its documentation for NON-COMMERCIAL purposes and without    * fee is hereby granted provided that this copyright notice    * appears in all copies.    *    * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY    * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED    * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A    * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE    * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,    * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.    */   /**    * This class defines a FieldInfo in the class file. Fields are used to    * describe instance variables in a class. The toString() method is    * augmented by a version that takes an array of ConstantPoolInfo    * objects (a constant pool). When a constant pool is available the    * toString() method generates a declaration for the field as it would    * appear in Java source.    *    * @version   1.3, 16 Aug 1995    * @author  Chuck McManis    * @see   ClassFile    */    class FieldInfo {       short   accessFlags;       ConstantPoolInfo  name;       ConstantPoolInfo  signature;       AttributeInfo attributes[];       public boolean read(DataInputStream di, ConstantPoolInfo pool[])     throws IOException {     int count;     accessFlags = di.readShort();     name = pool[di.readShort()];     signature = pool[di.readShort()];     count = di.readShort();     if (count != 0) {         attributes = new AttributeInfo[count];         for (int i = 0; i < count; i++) {       attributes[i] = new AttributeInfo();       if (! attributes[i].read(di, pool))           return (false);         }     }     return (true);       }       public void write(DataOutputStream dos, ConstantPoolInfo pool[])     throws IOException, Exception {     dos.writeShort(accessFlags);     dos.writeShort(ConstantPoolInfo.indexOf(name, pool));     dos.writeShort(ConstantPoolInfo.indexOf(signature, pool));     if (attributes == null) {         dos.writeShort(0);     } else {         dos.writeShort(attributes.length);         for (int i = 0; i < attributes.length; i++) {           attributes[i].write(dos, pool);         }     }       }       public String toString() {     StringBuffer x = new StringBuffer();     x.append(ClassFile.accessString(accessFlags));     x.append(ClassFile.typeString(signature.toString(), name.toString()));     if (attributes != null) {         x.append(" = "+attributes[0].toString());     }     return (x.toString());       }       public String toString(ConstantPoolInfo pool[]) {     StringBuffer x = new StringBuffer();     String  mytype;     x.append(ClassFile.accessString(accessFlags));     mytype = ClassFile.typeString(signature.toString(), name.toString());     x.append(mytype);     if (attributes != null) {         if (mytype.startsWith("boolean")) {       x.append(" "+attributes[0].toBoolean(pool));         } else             x.append(" "+attributes[0].toString(pool));     }     return (x.toString());       }   }