![]() |
|||
![]() ![]() |
![]() |
|
![]() |
Import statementsTheres no one place in a .class file where all the import statements are stored. To determine which import statements were in the source code, you have to list all the classes in the constant pool. You might choose to output one import statement for each class, or you might be somewhat more selective. In this example, I have chosen not to produce import statements for the class itself or any classes in java.lang. This makes the disassembled source code more similar to what you actually write in programs. If you wanted to, you could include import statements only for entire packages (for example, import java.util.*) rather than for individual classes. However, I find it convenient to be able to see exactly what classes a particular class references. To find the classes, you loop through the constant pool and check each entry to see if its a ClassInfo structure. Its important to remember that the zeroth entry in the constant pool is not included in the .class file. When a ClassInfo structure is found, you use its nameIndex() method to get the classs name as a UTF8 structure from the constant pool. Each name thus retrieved is tested to be sure that its not the name of this class and that its not a class from java.lang. Assuming neither of these is the case, an import statement for the class is printed. Listing 4-25 demonstrates the writeImports() method. Listing 4-25 The writeImports() method public void writeImports() { PoolEntry pe = null; String thisname = thePool.readUTF8(thisClass.nameIndex()); // recall that theres nothing in the zeroth pool entry for (int i = 1; i < thePool.howMany(); i++) { pe = thePool.read(i); if (pe.tag() == PoolEntry.cClassInfo) { ClassInfo ci = pe.readClassInfo(); String name = thePool.readUTF8(ci.nameIndex()); name = name.replace(/,.); postedif (!name.startsWith(java.lang.) && !name.equals(thisname)) { theOutput.println(import + name + ;); } } } theOutput.println(); } Access specifiersThe writeAccess() method looks at the access specifiers for the class and prints them in Java form. Listing 4-26 has the code. Listing 4-26 The writeAccess() method public void writeAccess() { if (isPublic) theOutput.print(public ); if (isFinal) theOutput.print(final ); if (isAbstract) theOutput.print(abstract ); if (isInterface) theOutput.print(interface ); else theOutput.print(class ); } Note that if a .class file is not an interface, then it must represent a class. Note also that one access flag, isSpecial, has no equivalent in Java source code. It exists only for the use of the compiler and the virtual machine. Class and superclassThe next thing you want to know is the name of the class. You can easily retrieve this from the thisClass field, which points to the name of the class in UTF8 format in the constant pool (see Listing 4-27). Listing 4-27 The writeClassName() method public void writeClassName() { String name = thePool.readUTF8(thisClass.nameIndex()); theOutput.print(name + ); } Next, you want to find out which class this class extends (see Listing 4-28). You have to watch out for the special case of java.lang.Object, which has no superclass. Otherwise, this is very similar to the previous method. Listing 4-28 The writeSuperclass() method public void writeSuperclass() { if (superclass.nameIndex() != 0) { String name = thePool.readUTF8(superclass.nameIndex()); theOutput.print(extends + name + ); } } InterfacesThe interfaces are similar except that there may be more than one of them. When youre finished outputting all the interfaces, open the class with an opening brace. The writeInterfaces() method is shown in Listing 4-29. Listing 4-29 The writeInterfaces() method public void writeInterfaces() { if (interfaces.length > 0) { String name = thePool.readUTF8(interfaces[0].nameIndex()); theOutput.print(implements + name + ); for (int i=1; i < interfaces.length; i++) { name = thePool.readUTF8(interfaces[i].nameIndex()); theOutput.print(, + name); } } theOutput.println( {); } Ive chosen to put the access specifiers, the class name, the class that this extends, all interfaces that this class implements, and the opening brace on a single line of the file. This produces output that looks like: public final class myVector extends java.util.Vector implements java.io.Serializable { Feel free to adjust this to match your preferences. For example, some people prefer to write each of these on separate lines. public final class myVector extends java.util.Vector implements java.io.Serializable { Both versions produce identical byte code, so when youre working backward from the byte code, theres no way to distinguish the two cases.
|
![]() |
|