![]() |
|||
![]() ![]() |
![]() |
![]()
|
![]() |
The 2-byte unsigned short immediately following the access flags is the name indexthat is, an index into the constant pool that provides the field names location. Next comes the descriptor index, another 2-byte unsigned short index into the constant pool. This points to a UTF8 structure, which represents a field descriptor. Next comes the attributes table for this field. You read this by passing the DataInputStream into the AttributeTable constructor. Listing 4-22 is the full FieldInfo class. Listing 4-22 The FieldInfo class import java.io.*; public class FieldInfo { int accessflags; int nameIndex; int descriptorIndex; AttributeInfo[] attributes; public final static int cPublic = 0x0001; public final static int cPrivate = 0x0002; public final static int cProtected = 0x0004; public final static int cStatic = 0x0008; public final static int cFinal = 0x0010; public final static int cVolatile = 0x0040; public final static int cTransient = 0x0080; public FieldInfo( DataInputStream dis) throws IOException { accessflags = dis.readUnsignedShort(); nameIndex = dis.readUnsignedShort(); descriptorIndex = dis.readUnsignedShort(); attributes = new AttributeInfo[dis.readUnsignedShort()]; for (int i = 0; i < attributes.length; i++) { attributes[i] = new AttributeInfo(dis); } } public int nameIndex() { return nameIndex; } public int descriptorIndex() { return descriptorIndex; } public boolean isPublic() { return (accessflags & cPublic) != 0; } public boolean isPrivate() { return (accessflags & cPrivate) != 0; } public boolean isProtected() { return (accessflags & cProtected) != 0; } public boolean isStatic() { return (accessflags & cStatic) != 0; } public boolean isVolatile() { return (accessflags & cVolatile) != 0; } public boolean isTransient() { return (accessflags & cTransient) != 0; } public boolean isFinal() { return (accessflags & cFinal) != 0; } } Heres the fleshed-out readFields() method for the Disassembler class. Its quite simple, because all the work goes on inside the FieldInfo class. FieldInfo[] fields; void readFields() throws IOException { fields = new FieldInfo[theInput.readUnsignedShort()]; for (int i = 0; i < fields.length; i++) { fields[i] = new FieldInfo[dis]; } } MethodsThe methods table is similar to the fields table. First, theres an unsigned short to tell you how many methods there are. Then theres an array of method_info structures. As with the FieldInfo structure, this program keeps all the intelligence inside the MethodInfo constructor. Listing 4-23 is the fleshed-out readMethods() method for the Disassembler class. Listing 4-23 readMethods() MethodInfo[] methods; void readMethods() throws IOException { methods = new MethodInfo[theInput.readUnsignedShort()]; for (int i = 0; i < methods.length; i++) { methods[i] = new MethodInfo[dis]; } } The MethodInfo structure is almost identical to a FieldInfo structure. In fact, the only difference is in the permitted values for the access flags and the meaning of the attributes. Listing 4-24 is the MethodInfo class. Listing 4-24 The MethodInfo class import java.io.*; public class MethodInfo { int accessflags; int nameIndex; int descriptorIndex; AttributeInfo[] attributes; public final static int cPublic = 0x0001; public final static int cPrivate = 0x0002; public final static int cProtected = 0x0004; public final static int cStatic = 0x0008; public final static int cFinal = 0x0010; public final static int cSynchronized = 0x0020; public final static int cNative = 0x0100; public final static int cAbstract = 0x0400; public MethodInfo (DataInputStream dis) throws IOException { accessflags = dis.readUnsignedShort(); nameIndex = dis.readUnsignedShort(); descriptorIndex = dis.readUnsignedShort(); attributes = new AttributeInfo[dis.readUnsignedShort()]; for (int i = 0; i < attributes.length; i++) { attributes[i] = new AttributeInfo(dis); } } public int nameIndex() { return nameIndex; } public int descriptorIndex() { return descriptorIndex; } public boolean isPublic() { return (accessflags & cPublic) != 0; } public boolean isPrivate() { return (accessflags & cPrivate) != 0; } public boolean isProtected() { return (accessflags & cProtected) != 0; } public boolean isStatic() { return (accessflags & cStatic) != 0; } public boolean isSynchronized() { return (accessflags & cSynchronized) != 0; } public boolean isNative() { return (accessflags & cNative) != 0; } public boolean isAbstract() { return (accessflags & cAbstract) != 0; } public AttributeInfo[] getAttributes() { return attributes; } public String toString() { return NameIndex: + nameIndex + ;\tDescriptorIndex: + descriptorIndex; } } Putting It All TogetherNow that the entire .class file has been read into memory and parsed, it can be output as more-or-less-legible source code. You do not need to output items in the order in which they appeared in the .class file. For example, the first thing outputted will be any import statements in the file. Then youll produce the access specifiers for the class and then the class name itself, followed by any interfaces that the class implements. Next come the fields, and then the methods. Along the way, youll add in necessary syntax such as semicolons and keywords that is normally present in source code but is not included in byte code. To do this, the Disassembler class needs for eight more methods to be filled out: writeImports(); writeAccess(); writeClassName(); writeInterfaces(); writeFields(); writeMethods(); Each of these methods will parse the data structures read in the first part of this chapter to collect the needed information.
|
![]() |
|