Main Page   Packages   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

CodeHTML.java

00001 package de.fub.bytecode.util;
00002 
00003 import de.fub.bytecode.classfile.*;
00004 import java.io.*;
00005 import java.util.BitSet;
00006 
00007 /**
00008  * Convert code into HTML file.
00009  *
00010  * @version $Id: CodeHTML.java,v 1.1.1.1 2002/01/24 03:41:42 pserver Exp $
00011  * @author  <A HREF="http://www.inf.fu-berlin.de/~dahm">M. Dahm</A>
00012  * 
00013  */
00014 final class CodeHTML implements de.fub.bytecode.Constants {
00015   private String    class_name;     // name of current class
00016   private Method[]  methods;    // Methods to print
00017   private PrintWriter   file;       // file to write to
00018   private BitSet        goto_set;
00019   private ConstantPool  constant_pool;
00020   private ConstantHTML  constant_html;
00021   private static boolean wide=false;
00022 
00023   CodeHTML(String dir, String class_name,
00024        Method[] methods, ConstantPool constant_pool,
00025        ConstantHTML constant_html) throws IOException
00026   {
00027     this.class_name     = class_name;
00028     this.methods    = methods;
00029     this.constant_pool  = constant_pool;
00030     this.constant_html = constant_html;
00031     
00032     file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
00033     file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
00034     
00035     for(int i=0; i < methods.length; i++)
00036       writeMethod(methods[i], i);
00037          
00038     file.println("</BODY></HTML>");
00039     file.close();
00040   }  
00041   /**
00042    * Disassemble a stream of byte codes and return the
00043    * string representation.
00044    *
00045    * @param  stream data input stream
00046    * @return String representation of byte code
00047    */
00048   private final String codeToHTML(ByteSequence bytes, int method_number)
00049        throws IOException
00050   {
00051     short        opcode = (short)bytes.readUnsignedByte();
00052     StringBuffer buf;
00053     String       name, sig, signature;
00054     int          default_offset=0, low, high;
00055     int          index, class_index, vindex, constant;
00056     int[]        jump_table;
00057     int          no_pad_bytes=0, offset;
00058 
00059     buf = new StringBuffer("<TT>" + OPCODE_NAMES[opcode] + "</TT></TD><TD>");
00060 
00061     /* Special case: Skip (0-3) padding bytes, i.e., the
00062      * following bytes are 4-byte-aligned
00063      */
00064     if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
00065       int remainder = bytes.getIndex() % 4;
00066       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
00067 
00068       for(int i=0; i < no_pad_bytes; i++)
00069     bytes.readByte();
00070 
00071       // Both cases have a field default_offset in common
00072       default_offset = bytes.readInt();
00073     }
00074 
00075     switch(opcode) {
00076     case TABLESWITCH:
00077       low  = bytes.readInt();
00078       high = bytes.readInt();
00079 
00080       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
00081       default_offset += offset;
00082 
00083       buf.append("<TABLE BORDER=1><TR>");
00084 
00085       // Print switch indices in first row (and default)
00086       jump_table = new int[high - low + 1];
00087       for(int i=0; i < jump_table.length; i++) {
00088     jump_table[i] = offset + bytes.readInt();
00089 
00090     buf.append("<TH>" + (low + i) + "</TH>");
00091       }
00092       buf.append("<TH>default</TH></TR>\n<TR>");
00093 
00094       // Print target and default indices in second row
00095       for(int i=0; i < jump_table.length; i++)
00096     buf.append("<TD><A HREF=\"#code" + method_number + "@" +
00097            jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
00098       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
00099          default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
00100 
00101       break;
00102 
00103       /* Lookup switch has variable length arguments.
00104        */
00105     case LOOKUPSWITCH:
00106       int npairs = bytes.readInt();
00107       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
00108       jump_table = new int[npairs];
00109       default_offset += offset;
00110 
00111       buf.append("<TABLE BORDER=1><TR>");
00112 
00113       // Print switch indices in first row (and default)
00114       for(int i=0; i < npairs; i++) {
00115     int match = bytes.readInt();
00116 
00117     jump_table[i] = offset + bytes.readInt();
00118     buf.append("<TH>" + match + "</TH>");
00119       }
00120       buf.append("<TH>default</TH></TR>\n<TR>");
00121 
00122       // Print target and default indices in second row
00123       for(int i=0; i < npairs; i++)
00124     buf.append("<TD><A HREF=\"#code" + method_number + "@" +
00125            jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
00126       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
00127          default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
00128       break;
00129 
00130       /* Two address bytes + offset from start of byte stream form the
00131        * jump target.
00132        */
00133     case GOTO:      case IFEQ:      case IFGE:      case IFGT:
00134     case IFLE:      case IFLT:
00135     case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
00136     case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
00137     case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
00138       
00139       index = (int)(bytes.getIndex() + bytes.readShort() - 1);
00140       
00141       buf.append("<A HREF=\"#code" + method_number + "@" + index + "\">" + index + "</A>");
00142       break;
00143       
00144       /* Same for 32-bit wide jumps
00145        */
00146     case GOTO_W: case JSR_W:
00147       int windex = bytes.getIndex() + bytes.readInt() - 1;
00148       buf.append("<A HREF=\"#code" + method_number + "@" + windex + "\">" +
00149          windex + "</A>");
00150       break;
00151 
00152       /* Index byte references local variable (register)
00153        */
00154     case ALOAD:  case ASTORE: case DLOAD:  case DSTORE: case FLOAD:
00155     case FSTORE: case ILOAD:  case ISTORE: case LLOAD:  case LSTORE:
00156     case RET: 
00157       if(wide) {
00158     vindex = bytes.readShort();
00159     wide=false; // Clear flag
00160       }
00161       else
00162     vindex = bytes.readUnsignedByte();
00163 
00164       buf.append("%" + vindex);
00165       break;
00166 
00167       /*
00168        * Remember wide byte which is used to form a 16-bit address in the
00169        * following instruction. Relies on that the method is called again with
00170        * the following opcode.
00171        */
00172     case WIDE:
00173       wide      = true;
00174       buf.append("(wide)");
00175       break;
00176 
00177       /* Array of basic type.
00178        */
00179     case NEWARRAY:
00180       buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>");
00181       break;
00182 
00183       /* Access object/class fields.
00184        */
00185     case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC:
00186       index = bytes.readShort();
00187       ConstantFieldref c1 = (ConstantFieldref)constant_pool.getConstant(index, CONSTANT_Fieldref);
00188 
00189       class_index = c1.getClassIndex();
00190       name = constant_pool.getConstantString(class_index, CONSTANT_Class);
00191       name = Utility.compactClassName(name, false);
00192 
00193       index = c1.getNameAndTypeIndex();
00194       String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType);
00195 
00196       if(name.equals(class_name)) { // Local field
00197     buf.append("<A HREF=\"" + class_name + "_methods.html#field" + field_name +
00198            "\" TARGET=Methods>" + field_name + "</A>\n");
00199       }
00200       else
00201     buf.append(constant_html.referenceConstant(class_index) + "." + field_name);
00202             
00203       break;
00204       
00205       /* Operands are references to classes in constant pool
00206        */
00207     case CHECKCAST: case INSTANCEOF: case NEW:
00208       index = bytes.readShort();
00209       buf.append(constant_html.referenceConstant(index));
00210       break;
00211    
00212       /* Operands are references to methods in constant pool
00213        */
00214     case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE:
00215       int m_index = bytes.readShort();
00216       String str;
00217 
00218       if(opcode == INVOKEINTERFACE) { // Special treatment needed
00219     int nargs    = bytes.readUnsignedByte(); // Redundant
00220     int reserved = bytes.readUnsignedByte(); // Reserved
00221 
00222     ConstantInterfaceMethodref c=(ConstantInterfaceMethodref)constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref);
00223 
00224     class_index = c.getClassIndex();
00225     str = constant_pool.constantToString(c);
00226     index = c.getNameAndTypeIndex();
00227       }
00228       else {
00229     ConstantMethodref c = (ConstantMethodref)constant_pool.getConstant(m_index, CONSTANT_Methodref);
00230     class_index = c.getClassIndex();
00231             
00232     str  = constant_pool.constantToString(c);
00233     index = c.getNameAndTypeIndex();
00234       }
00235             
00236       name = Class2HTML.referenceClass(class_index);
00237       str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(index, CONSTANT_NameAndType)));
00238 
00239       // Get signature, i.e., types
00240       ConstantNameAndType c2 = (ConstantNameAndType)constant_pool.
00241     getConstant(index, CONSTANT_NameAndType);
00242       signature = constant_pool.constantToString(c2.getSignatureIndex(),
00243                          CONSTANT_Utf8);
00244       String[] args = Utility.methodSignatureArgumentTypes(signature, false);
00245       String   type = Utility.methodSignatureReturnType(signature, false);
00246 
00247       buf.append(name + ".<A HREF=\"" + class_name + "_cp.html#cp" + m_index + 
00248          "\" TARGET=ConstantPool>" + str + "</A>" + "(");
00249 
00250       // List arguments
00251       for(int i=0; i < args.length; i++) {
00252     buf.append(Class2HTML.referenceType(args[i]));
00253       
00254     if(i < args.length - 1)
00255       buf.append(", ");
00256       }
00257       // Attach return type
00258       buf.append("):" + Class2HTML.referenceType(type));
00259 
00260       break;
00261         
00262       /* Operands are references to items in constant pool
00263        */
00264     case LDC_W: case LDC2_W:
00265       index = bytes.readShort();
00266 
00267       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 
00268          "\" TARGET=\"ConstantPool\">" +
00269          Class2HTML.toHTML(constant_pool.constantToString(index, 
00270                                   constant_pool.
00271                                   getConstant(index).getTag()))+
00272          "</a>");
00273       break;
00274 
00275     case LDC:
00276       index = bytes.readUnsignedByte();
00277       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 
00278          "\" TARGET=\"ConstantPool\">" +
00279          Class2HTML.toHTML(constant_pool.constantToString(index, 
00280                                   constant_pool.
00281                                   getConstant(index).getTag()))+
00282          "</a>");
00283       break;
00284     
00285       /* Array of references.
00286        */
00287     case ANEWARRAY:
00288       index = bytes.readShort();
00289       
00290       buf.append(constant_html.referenceConstant(index));
00291       break;
00292     
00293       /* Multidimensional array of references.
00294        */
00295     case MULTIANEWARRAY:
00296       index = bytes.readShort();
00297       int dimensions = bytes.readByte();
00298       buf.append(constant_html.referenceConstant(index) + ":" + dimensions + "-dimensional");
00299       break;
00300 
00301       /* Increment local variable.
00302        */
00303     case IINC:
00304       if(wide) {
00305     vindex   = bytes.readShort();
00306     constant = bytes.readShort();
00307     wide     = false;
00308       }
00309       else {
00310     vindex   = bytes.readUnsignedByte();
00311     constant = bytes.readByte();
00312       }
00313       buf.append("%" + vindex + " " + constant);
00314       break;
00315 
00316     default:
00317       if(NO_OF_OPERANDS[opcode] > 0) {
00318     for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
00319       switch(TYPE_OF_OPERANDS[opcode][i]) {
00320       case T_BYTE:
00321         buf.append(bytes.readUnsignedByte());
00322         break;
00323 
00324       case T_SHORT: // Either branch or index
00325         buf.append(bytes.readShort());
00326         break;
00327 
00328       case T_INT:
00329         buf.append(bytes.readInt());
00330         break;
00331                           
00332       default: // Never reached
00333         System.err.println("Unreachable default case reached!");
00334         System.exit(-1);
00335       }
00336       buf.append("&nbsp;");
00337     }
00338       }
00339     }
00340 
00341     buf.append("</TD>");
00342     return buf.toString();
00343   }  
00344   /**
00345    * Find all target addresses in code, so that they can be marked
00346    * with &lt;A NAME = ...&gt;. Target addresses are kept in an BitSet object.
00347    */
00348   private final void findGotos(ByteSequence bytes, Method method, Code code) 
00349        throws IOException
00350   {
00351     int index;
00352     goto_set = new BitSet(bytes.available());
00353     int opcode;
00354 
00355     /* First get Code attribute from method and the exceptions handled
00356      * (try .. catch) in this method. We only need the line number here.
00357      */
00358     
00359     if(code != null) {
00360       CodeException[] ce  = code.getExceptionTable();
00361       int             len = ce.length;
00362 
00363       for(int i=0; i < len; i++) {
00364     goto_set.set(ce[i].getStartPC());
00365     goto_set.set(ce[i].getEndPC());
00366     goto_set.set(ce[i].getHandlerPC());
00367       }
00368 
00369       // Look for local variables and their range
00370       Attribute[] attributes = code.getAttributes();
00371       for(int i=0; i < attributes.length; i++) {
00372     if(attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) {
00373       LocalVariable[] vars = ((LocalVariableTable)attributes[i]).getLocalVariableTable();
00374 
00375       for(int j=0; j < vars.length; j++) {
00376         int  start = vars[j].getStartPC();
00377         int  end   = (int)(start + vars[j].getLength());
00378         goto_set.set(start);
00379         goto_set.set(end);
00380       }
00381       break;
00382     }
00383       }
00384     }
00385 
00386     // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
00387     for(int i=0; bytes.available() > 0; i++) {
00388       opcode = bytes.readUnsignedByte();
00389       //System.out.println(OPCODE_NAMES[opcode]);
00390       switch(opcode) {
00391       case TABLESWITCH: case LOOKUPSWITCH:
00392     //bytes.readByte(); // Skip already read byte
00393 
00394     int remainder = bytes.getIndex() % 4;
00395     int no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
00396     int default_offset, offset;
00397 
00398     for(int j=0; j < no_pad_bytes; j++)
00399       bytes.readByte();
00400 
00401     // Both cases have a field default_offset in common
00402     default_offset = bytes.readInt();
00403 
00404     if(opcode == TABLESWITCH) {
00405       int low = bytes.readInt();
00406       int high = bytes.readInt();
00407 
00408       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
00409       default_offset += offset;
00410       goto_set.set(default_offset);
00411 
00412       for(int j=0; j < (high - low + 1); j++) {
00413         index = offset + bytes.readInt();
00414         goto_set.set(index);
00415       }
00416     }
00417     else { // LOOKUPSWITCH
00418       int npairs = bytes.readInt();
00419 
00420       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
00421       default_offset += offset;
00422       goto_set.set(default_offset);
00423 
00424       for(int j=0; j < npairs; j++) {
00425         int match = bytes.readInt();
00426 
00427         index = offset + bytes.readInt();
00428         goto_set.set(index);
00429       }
00430     }
00431     break;
00432     
00433       case GOTO:      case IFEQ:      case IFGE:      case IFGT:
00434       case IFLE:      case IFLT:
00435       case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
00436       case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
00437       case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
00438     //bytes.readByte(); // Skip already read byte
00439     index = bytes.getIndex() + bytes.readShort() - 1;
00440       
00441     goto_set.set(index);
00442     break;
00443 
00444       case GOTO_W: case JSR_W:
00445     //bytes.readByte(); // Skip already read byte
00446     index = bytes.getIndex() + bytes.readInt() - 1;
00447     goto_set.set(index);
00448     break;
00449 
00450       default:
00451     bytes.unreadByte();
00452     codeToHTML(bytes, 0); // Ignore output
00453       }
00454     }
00455   }  
00456   /**
00457    * Write a single method with the byte code associated with it.
00458    */
00459   private void writeMethod(Method method, int method_number)
00460        throws IOException
00461   {
00462     // Get raw signature
00463     String       signature = method.getSignature();
00464     // Get array of strings containing the argument types
00465     String[]     args      = Utility.methodSignatureArgumentTypes(signature, false);
00466     // Get return type string
00467     String       type      = Utility.methodSignatureReturnType(signature, false);
00468     // Get method name
00469     String       name      = method.getName();
00470     String       html_name = Class2HTML.toHTML(name);
00471     // Get method's access flags
00472     String       access    = Utility.accessToString(method.getAccessFlags());
00473     access = Utility.replace(access, " ", "&nbsp;");
00474     // Get the method's attributes, the Code Attribute in particular
00475     Attribute[]  attributes= method.getAttributes();    
00476 
00477     file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT>&nbsp;" +
00478            "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) +
00479            "</A>&nbsp<A HREF=\"" + class_name + "_methods.html#method" + method_number +
00480            "\" TARGET=Methods>" + html_name + "</A>(");
00481 
00482     for(int i=0; i < args.length; i++) {
00483       file.print(Class2HTML.referenceType(args[i]));
00484       if(i < args.length - 1)
00485     file.print(",&nbsp;");
00486     }
00487 
00488     file.println(")</B></P>");
00489         
00490     Code c=null;
00491     byte[] code=null;
00492 
00493     if(attributes.length > 0) {
00494       file.print("<H4>Attributes</H4><UL>\n");
00495       for(int i=0; i < attributes.length; i++) {
00496     byte tag = attributes[i].getTag();
00497 
00498     if(tag != ATTR_UNKNOWN)
00499       file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i +
00500              "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
00501     else
00502       file.print("<LI>" + attributes[i] + "</LI>");
00503 
00504     if(tag == ATTR_CODE) {
00505       c = (Code)attributes[i];
00506       Attribute[] attributes2 = c.getAttributes();
00507       code                              = c.getCode();
00508                     
00509       file.print("<UL>");
00510       for(int j=0; j < attributes2.length; j++) {
00511         tag = attributes2[j].getTag();
00512         file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" +
00513                "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" +
00514                ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
00515 
00516       }
00517       file.print("</UL>");
00518     }
00519       }
00520       file.println("</UL>");
00521     }
00522 
00523     if(code != null) { // No code, an abstract method, e.g.
00524       //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));
00525 
00526       // Print the byte code
00527       ByteSequence stream = new ByteSequence(code);
00528       stream.mark(stream.available());
00529       findGotos(stream, method, c);
00530       stream.reset();
00531 
00532       file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" +
00533            "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
00534 
00535       for(int i=0; stream.available() > 0; i++) {
00536     int offset = stream.getIndex();
00537     String str = codeToHTML(stream, method_number);
00538     String anchor = "";
00539 
00540     /* Set an anchor mark if this line is targetted by a goto, jsr, etc.
00541      * Defining an anchor for every line is very inefficient!
00542      */
00543     if(goto_set.get(offset))
00544       anchor = "<A NAME=code" + method_number + "@" + offset +  "></A>";
00545 
00546     String anchor2;
00547     if(stream.getIndex() == code.length) // last loop
00548       anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
00549     else
00550       anchor2 = "" + offset;
00551 
00552     file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
00553       }
00554       
00555       // Mark last line, may be targetted from Attributes window
00556       file.println("<TR><TD> </A></TD></TR>");
00557       file.println("</TABLE>");
00558     }
00559 
00560   }  
00561 }

Generated at Thu Feb 7 06:42:39 2002 for Bandera by doxygen1.2.10 written by Dimitri van Heesch, © 1997-2001