Java tutorial
/* * 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 <init> 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()); } }