Fields
Only two parts of the file are left: the fields and the methods. Lets look at the fields first. Its not at all uncommon for a class to have many fields. You therefore need to loop through all the fields with a for loop. Inside the loop, you check the access specifiers, the name, and the type of each field.
To read the name of the field, you simply read the UTF8 structure in the constant pool at the fields name index. The type of the field requires more effort. Although it is stored as a UTF8 string in the constant pool at the FieldInfos descriptorIndex, the UTF8 string needs to be decoded first. Primitive types like int or char are encoded as single letters. For example, an int is the capital letter I. Table 4-4 lists the encodings for the primitive types.
Table 4-4 Primitive type encodings
|
B
| byte
|
C
| char
|
D
| double
|
F
| float
|
I
| int
|
J
| long
|
S
| short
|
Z
| boolean
|
|
Class types are encoded as the capital letter L, followed by the fully qualified class name, followed by a semicolon. Furthermore, for historical reasons, the periods in the fully qualified class name change to forward slashes. Therefore, inside the constant pool, the String class is written as Ljava/lang/String;, the Object class is written as Ljava/lang/Object;, the Vector class is written Ljava/util/Vector;, and so on. Converting this into the format you expect is easy. Just trim the first and last characters of the string with the substring() method and use the replace() method to change the slashes to periods like this:
String s = Ljava/lang/String;;
String r = d.substring(1, r.length() - 1);
r = r.replace(/, .);
The final type you need to deal with are the array types. These are encoded by prefixing the type of the array with left bracket signs ([), one for each dimension in the array. Thus, a double[] array is encoded as [double. A String[][] array is encoded as [[Ljava/lang/String;. To decode array types, you first count the number of left brackets and then recursively call the decodeDescriptor() method. Listing 4-30 shows the complete decodeDescriptor() method. It takes a single argument the string to be decoded and returns the decoded string.
Listing 4-30 The decodeDescriptor() method
public String decodeDescriptor(String d) {
if (d.equals(B)) return byte;
else if (d.equals(C)) return char;
else if (d.equals(D)) return double;
else if (d.equals(F)) return float;
else if (d.equals(I)) return int;
else if (d.equals(J)) return long;
else if (d.equals(S)) return short;
else if (d.equals(Z)) return boolean;
else if (d.startsWith(L)) { // object
String r = d.substring(1, r.length() - 1);
r = r.replace(/, .);
return r;
}
else if (d.startsWith([)) { // array
int dimensions = d.lastIndexOf([) + 1;
String type = decodeDescriptor(d.substring(dimensions));
for (int i=0; i < dimensions; i++) {
type += [];
}
return type;
}
else {
throw new ClassFormatError(Unrecognized Type: + d);
}
}
Now that you have a method to decode descriptors, its easy to finish the writeFields() method. Listing 4-31 demonstrates.
Listing 4-31 The writeFields() method
public void writeFields() {
for (int i = 0; i < fields.length; i++) {
// indent two spaces
theOutput.print( );
// print the access specifiers
if (fields[i].isPublic()) theOutput.print(public );
if (fields[i].isPrivate()) theOutput.print(private );
if (fields[i].isProtected()) theOutput.print(protected );
if (fields[i].isStatic()) theOutput.print(static );
if (fields[i].isVolatile()) theOutput.print(volatile );
if (fields[i].isTransient()) theOutput.print(transient );
if (fields[i].isFinal()) theOutput.print(final );
//print the type
String descriptor = thePool.readUTF8(fields[i].descriptorIndex());
theOutput.print(decodeDescriptor(descriptor) + );
//print the name
theOutput.print(thePool.readUTF8(fields[i].nameIndex()));
theOutput.println(;);
}
}
Note: I debated whether to include the code to read a field info structure and convert it into a string in the FieldInfo class or in the Disassembler class. Although it would make somewhat more sense to encapsulate the code in the FieldInfo class, it can be decoded only if each FieldInfo object carries a reference to its constant pool.
|