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 <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());
}
}
Related examples in the same category