final void writeAttribute(Attribute attribute, String anchor, int method_number) throws IOException { byte tag = attribute.getTag(); int index; if (tag == ATTR_UNKNOWN) // Don't know what to do about this one return; attr_count++; // Increment number of attributes found so far if (attr_count % 2 == 0) file.print("<TR BGCOLOR=\"#C0C0C0\"><TD>"); else file.print("<TR BGCOLOR=\"#A0A0A0\"><TD>"); file.println( "<H4><A NAME=\"" + anchor + "\">" + attr_count + " " + ATTRIBUTE_NAMES[tag] + "</A></H4>"); /* Handle different attributes */ switch (tag) { case ATTR_CODE: Code c = (Code) attribute; // Some directly printable values file.print( "<UL><LI>Maximum stack size = " + c.getMaxStack() + "</LI>\n<LI>Number of local variables = " + c.getMaxLocals() + "</LI>\n<LI><A HREF=\"" + class_name + "_code.html#method" + method_number + "\" TARGET=Code>Byte code</A></LI></UL>\n"); // Get handled exceptions and list them CodeException[] ce = c.getExceptionTable(); int len = ce.length; if (len > 0) { file.print("<P><B>Exceptions handled</B><UL>"); for (int i = 0; i < len; i++) { int catch_type = ce[i].getCatchType(); // Index in constant pool file.print("<LI>"); if (catch_type != 0) file.print(constant_html.referenceConstant(catch_type)); // Create Link to _cp.html else file.print("Any Exception"); file.print( "<BR>(Ranging from lines " + codeLink(ce[i].getStartPC(), method_number) + " to " + codeLink(ce[i].getEndPC(), method_number) + ", handled at line " + codeLink(ce[i].getHandlerPC(), method_number) + ")</LI>"); } file.print("</UL>"); } break; case ATTR_CONSTANT_VALUE: index = ((ConstantValue) attribute).getConstantValueIndex(); // Reference _cp.html file.print( "<UL><LI><A HREF=\"" + class_name + "_cp.html#cp" + index + "\" TARGET=\"ConstantPool\">Constant value index(" + index + ")</A></UL>\n"); break; case ATTR_SOURCE_FILE: index = ((SourceFile) attribute).getSourceFileIndex(); // Reference _cp.html file.print( "<UL><LI><A HREF=\"" + class_name + "_cp.html#cp" + index + "\" TARGET=\"ConstantPool\">Source file index(" + index + ")</A></UL>\n"); break; case ATTR_EXCEPTIONS: // List thrown exceptions int[] indices = ((ExceptionTable) attribute).getExceptionIndexTable(); file.print("<UL>"); for (int i = 0; i < indices.length; i++) file.print( "<LI><A HREF=\"" + class_name + "_cp.html#cp" + indices[i] + "\" TARGET=\"ConstantPool\">Exception class index(" + indices[i] + ")</A>\n"); file.print("</UL>\n"); break; case ATTR_LINE_NUMBER_TABLE: LineNumber[] line_numbers = ((LineNumberTable) attribute).getLineNumberTable(); // List line number pairs file.print("<P>"); for (int i = 0; i < line_numbers.length; i++) { file.print( "(" + line_numbers[i].getStartPC() + ", " + line_numbers[i].getLineNumber() + ")"); if (i < line_numbers.length - 1) file.print(", "); // breakable } break; case ATTR_LOCAL_VARIABLE_TABLE: LocalVariable[] vars = ((LocalVariableTable) attribute).getLocalVariableTable(); // List name, range and type file.print("<UL>"); for (int i = 0; i < vars.length; i++) { index = vars[i].getSignatureIndex(); String signature = ((ConstantUtf8) constant_pool.getConstant(index, CONSTANT_Utf8)).getBytes(); signature = Utility.signatureToString(signature, false); int start = vars[i].getStartPC(); int end = (start + vars[i].getLength()); file.println( "<LI>" + Class2HTML.referenceType(signature) + " <B>" + vars[i].getName() + "</B> in slot %" + vars[i].getIndex() + "<BR>Valid from lines " + "<A HREF=\"" + class_name + "_code.html#code" + method_number + "@" + start + "\" TARGET=Code>" + start + "</A> to " + "<A HREF=\"" + class_name + "_code.html#code" + method_number + "@" + end + "\" TARGET=Code>" + end + "</A></LI>"); } file.print("</UL>\n"); break; case ATTR_INNER_CLASSES: InnerClass[] classes = ((InnerClasses) attribute).getInnerClasses(); // List inner classes file.print("<UL>"); for (int i = 0; i < classes.length; i++) { String name, access; index = classes[i].getInnerNameIndex(); if (index > 0) name = ((ConstantUtf8) constant_pool.getConstant(index, CONSTANT_Utf8)).getBytes(); else name = "<anonymous>"; access = Utility.accessToString(classes[i].getInnerAccessFlags()); file.print( "<LI><FONT COLOR=\"#FF0000\">" + access + "</FONT> " + constant_html.referenceConstant(classes[i].getInnerClassIndex()) + " in class " + constant_html.referenceConstant(classes[i].getOuterClassIndex()) + " named " + name + "</LI>\n"); } file.print("</UL>\n"); break; default: // Such as Unknown attribute or Deprecated file.print("<P>" + attribute.toString()); } file.println("</TD></TR>"); file.flush(); }
/** Write a single method with the byte code associated with it. */ private void writeMethod(Method method, int method_number) throws IOException { // Get raw signature String signature = method.getSignature(); // Get array of strings containing the argument types String[] args = Utility.methodSignatureArgumentTypes(signature, false); // Get return type string String type = Utility.methodSignatureReturnType(signature, false); // Get method name String name = method.getName(); String html_name = Class2HTML.toHTML(name); // Get method's access flags String access = Utility.accessToString(method.getAccessFlags()); access = Utility.replace(access, " ", " "); // Get the method's attributes, the Code Attribute in particular Attribute[] attributes = method.getAttributes(); file.print( "<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " + "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) + "</A> <A HREF=\"" + class_name + "_methods.html#method" + method_number + "\" TARGET=Methods>" + html_name + "</A>("); for (int i = 0; i < args.length; i++) { file.print(Class2HTML.referenceType(args[i])); if (i < args.length - 1) { file.print(", "); } } file.println(")</B></P>"); Code c = null; byte[] code = null; if (attributes.length > 0) { file.print("<H4>Attributes</H4><UL>\n"); for (int i = 0; i < attributes.length; i++) { byte tag = attributes[i].getTag(); if (tag != ATTR_UNKNOWN) { file.print( "<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i + "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n"); } else { file.print("<LI>" + attributes[i] + "</LI>"); } if (tag == ATTR_CODE) { c = (Code) attributes[i]; Attribute[] attributes2 = c.getAttributes(); code = c.getCode(); file.print("<UL>"); for (int j = 0; j < attributes2.length; j++) { tag = attributes2[j].getTag(); file.print( "<LI><A HREF=\"" + class_name + "_attributes.html#" + "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n"); } file.print("</UL>"); } } file.println("</UL>"); } if (code != null) { // No code, an abstract method, e.g. // System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1)); // Print the byte code ByteSequence stream = new ByteSequence(code); stream.mark(stream.available()); findGotos(stream, method, c); stream.reset(); file.println( "<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" + "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>"); for (int i = 0; stream.available() > 0; i++) { int offset = stream.getIndex(); String str = codeToHTML(stream, method_number); String anchor = ""; /* Set an anchor mark if this line is targetted by a goto, jsr, etc. * Defining an anchor for every line is very inefficient! */ if (goto_set.get(offset)) { anchor = "<A NAME=code" + method_number + "@" + offset + "></A>"; } String anchor2; if (stream.getIndex() == code.length) { anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>"; } else { anchor2 = "" + offset; } file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>"); } // Mark last line, may be targetted from Attributes window file.println("<TR><TD> </A></TD></TR>"); file.println("</TABLE>"); } }
/** * Disassemble a stream of byte codes and return the string representation. * * @param stream data input stream * @return String representation of byte code */ private final String codeToHTML(ByteSequence bytes, int method_number) throws IOException { short opcode = (short) bytes.readUnsignedByte(); StringBuilder buf; String name, signature; int default_offset = 0, low, high; int index, class_index, vindex, constant; int[] jump_table; int no_pad_bytes = 0, offset; buf = new StringBuilder(256); buf.append("<TT>").append(OPCODE_NAMES[opcode]).append("</TT></TD><TD>"); /* Special case: Skip (0-3) padding bytes, i.e., the * following bytes are 4-byte-aligned */ if ((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) { int remainder = bytes.getIndex() % 4; no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder; for (int i = 0; i < no_pad_bytes; i++) { bytes.readByte(); } // Both cases have a field default_offset in common default_offset = bytes.readInt(); } switch (opcode) { case TABLESWITCH: low = bytes.readInt(); high = bytes.readInt(); offset = bytes.getIndex() - 12 - no_pad_bytes - 1; default_offset += offset; buf.append("<TABLE BORDER=1><TR>"); // Print switch indices in first row (and default) jump_table = new int[high - low + 1]; for (int i = 0; i < jump_table.length; i++) { jump_table[i] = offset + bytes.readInt(); buf.append("<TH>").append(low + i).append("</TH>"); } buf.append("<TH>default</TH></TR>\n<TR>"); // Print target and default indices in second row for (int i = 0; i < jump_table.length; i++) { buf.append("<TD><A HREF=\"#code") .append(method_number) .append("@") .append(jump_table[i]) .append("\">") .append(jump_table[i]) .append("</A></TD>"); } buf.append("<TD><A HREF=\"#code") .append(method_number) .append("@") .append(default_offset) .append("\">") .append(default_offset) .append("</A></TD></TR>\n</TABLE>\n"); break; /* Lookup switch has variable length arguments. */ case LOOKUPSWITCH: int npairs = bytes.readInt(); offset = bytes.getIndex() - 8 - no_pad_bytes - 1; jump_table = new int[npairs]; default_offset += offset; buf.append("<TABLE BORDER=1><TR>"); // Print switch indices in first row (and default) for (int i = 0; i < npairs; i++) { int match = bytes.readInt(); jump_table[i] = offset + bytes.readInt(); buf.append("<TH>").append(match).append("</TH>"); } buf.append("<TH>default</TH></TR>\n<TR>"); // Print target and default indices in second row for (int i = 0; i < npairs; i++) { buf.append("<TD><A HREF=\"#code") .append(method_number) .append("@") .append(jump_table[i]) .append("\">") .append(jump_table[i]) .append("</A></TD>"); } buf.append("<TD><A HREF=\"#code") .append(method_number) .append("@") .append(default_offset) .append("\">") .append(default_offset) .append("</A></TD></TR>\n</TABLE>\n"); break; /* Two address bytes + offset from start of byte stream form the * jump target. */ case GOTO: case IFEQ: case IFGE: case IFGT: case IFLE: case IFLT: case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ: case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR: index = (int) (bytes.getIndex() + bytes.readShort() - 1); buf.append("<A HREF=\"#code") .append(method_number) .append("@") .append(index) .append("\">") .append(index) .append("</A>"); break; /* Same for 32-bit wide jumps */ case GOTO_W: case JSR_W: int windex = bytes.getIndex() + bytes.readInt() - 1; buf.append("<A HREF=\"#code") .append(method_number) .append("@") .append(windex) .append("\">") .append(windex) .append("</A>"); break; /* Index byte references local variable (register) */ case ALOAD: case ASTORE: case DLOAD: case DSTORE: case FLOAD: case FSTORE: case ILOAD: case ISTORE: case LLOAD: case LSTORE: case RET: if (wide) { vindex = bytes.readShort(); wide = false; // Clear flag } else { vindex = bytes.readUnsignedByte(); } buf.append("%").append(vindex); break; /* * Remember wide byte which is used to form a 16-bit address in the * following instruction. Relies on that the method is called again with * the following opcode. */ case WIDE: wide = true; buf.append("(wide)"); break; /* Array of basic type. */ case NEWARRAY: buf.append("<FONT COLOR=\"#00FF00\">") .append(TYPE_NAMES[bytes.readByte()]) .append("</FONT>"); break; /* Access object/class fields. */ case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC: index = bytes.readShort(); ConstantFieldref c1 = (ConstantFieldref) constant_pool.getConstant(index, CONSTANT_Fieldref); class_index = c1.getClassIndex(); name = constant_pool.getConstantString(class_index, CONSTANT_Class); name = Utility.compactClassName(name, false); index = c1.getNameAndTypeIndex(); String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType); if (name.equals(class_name)) { // Local field buf.append("<A HREF=\"") .append(class_name) .append("_methods.html#field") .append(field_name) .append("\" TARGET=Methods>") .append(field_name) .append("</A>\n"); } else { buf.append(constant_html.referenceConstant(class_index)).append(".").append(field_name); } break; /* Operands are references to classes in constant pool */ case CHECKCAST: case INSTANCEOF: case NEW: index = bytes.readShort(); buf.append(constant_html.referenceConstant(index)); break; /* Operands are references to methods in constant pool */ case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE: int m_index = bytes.readShort(); String str; if (opcode == INVOKEINTERFACE) { // Special treatment needed int nargs = bytes.readUnsignedByte(); // Redundant int reserved = bytes.readUnsignedByte(); // Reserved ConstantInterfaceMethodref c = (ConstantInterfaceMethodref) constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref); class_index = c.getClassIndex(); str = constant_pool.constantToString(c); index = c.getNameAndTypeIndex(); } else { ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(m_index, CONSTANT_Methodref); class_index = c.getClassIndex(); str = constant_pool.constantToString(c); index = c.getNameAndTypeIndex(); } name = Class2HTML.referenceClass(class_index); str = Class2HTML.toHTML( constant_pool.constantToString( constant_pool.getConstant(index, CONSTANT_NameAndType))); // Get signature, i.e., types ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(index, CONSTANT_NameAndType); signature = constant_pool.constantToString(c2.getSignatureIndex(), CONSTANT_Utf8); String[] args = Utility.methodSignatureArgumentTypes(signature, false); String type = Utility.methodSignatureReturnType(signature, false); buf.append(name) .append(".<A HREF=\"") .append(class_name) .append("_cp.html#cp") .append(m_index) .append("\" TARGET=ConstantPool>") .append(str) .append("</A>") .append("("); // List arguments for (int i = 0; i < args.length; i++) { buf.append(Class2HTML.referenceType(args[i])); if (i < args.length - 1) { buf.append(", "); } } // Attach return type buf.append("):").append(Class2HTML.referenceType(type)); break; /* Operands are references to items in constant pool */ case LDC_W: case LDC2_W: index = bytes.readShort(); buf.append("<A HREF=\"") .append(class_name) .append("_cp.html#cp") .append(index) .append("\" TARGET=\"ConstantPool\">") .append( Class2HTML.toHTML( constant_pool.constantToString( index, constant_pool.getConstant(index).getTag()))) .append("</a>"); break; case LDC: index = bytes.readUnsignedByte(); buf.append("<A HREF=\"") .append(class_name) .append("_cp.html#cp") .append(index) .append("\" TARGET=\"ConstantPool\">") .append( Class2HTML.toHTML( constant_pool.constantToString( index, constant_pool.getConstant(index).getTag()))) .append("</a>"); break; /* Array of references. */ case ANEWARRAY: index = bytes.readShort(); buf.append(constant_html.referenceConstant(index)); break; /* Multidimensional array of references. */ case MULTIANEWARRAY: index = bytes.readShort(); int dimensions = bytes.readByte(); buf.append(constant_html.referenceConstant(index)) .append(":") .append(dimensions) .append("-dimensional"); break; /* Increment local variable. */ case IINC: if (wide) { vindex = bytes.readShort(); constant = bytes.readShort(); wide = false; } else { vindex = bytes.readUnsignedByte(); constant = bytes.readByte(); } buf.append("%").append(vindex).append(" ").append(constant); break; default: if (NO_OF_OPERANDS[opcode] > 0) { for (int i = 0; i < TYPE_OF_OPERANDS[opcode].length; i++) { switch (TYPE_OF_OPERANDS[opcode][i]) { case T_BYTE: buf.append(bytes.readUnsignedByte()); break; case T_SHORT: // Either branch or index buf.append(bytes.readShort()); break; case T_INT: buf.append(bytes.readInt()); break; default: // Never reached System.err.println("Unreachable default case reached!"); System.exit(-1); } buf.append(" "); } } } buf.append("</TD>"); return buf.toString(); }
private void writeConstant(int index) { byte tag = constants[index].getTag(); int class_index, name_index; String ref; // The header is always the same file.println("<H4> <A NAME=cp" + index + ">" + index + "</A> " + CONSTANT_NAMES[tag] + "</H4>"); /* For every constant type get the needed parameters and print them appropiately */ switch (tag) { case CONSTANT_InterfaceMethodref: case CONSTANT_Methodref: // Get class_index and name_and_type_index, depending on type if (tag == CONSTANT_Methodref) { ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(index, CONSTANT_Methodref); class_index = c.getClassIndex(); name_index = c.getNameAndTypeIndex(); } else { ConstantInterfaceMethodref c1 = (ConstantInterfaceMethodref) constant_pool.getConstant(index, CONSTANT_InterfaceMethodref); class_index = c1.getClassIndex(); name_index = c1.getNameAndTypeIndex(); } // Get method name and its class String method_name = constant_pool.constantToString(name_index, CONSTANT_NameAndType); String html_method_name = Class2HTML.toHTML(method_name); // Partially compacted class name, i.e., / -> . String method_class = constant_pool.constantToString(class_index, CONSTANT_Class); String short_method_class = Utility.compactClassName(method_class); // I.e., remove java.lang. short_method_class = Utility.compactClassName(method_class); // I.e., remove java.lang. short_method_class = Utility.compactClassName( short_method_class, class_package + ".", true); // Remove class package prefix // Get method signature ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(name_index, CONSTANT_NameAndType); String signature = constant_pool.constantToString(c2.getSignatureIndex(), CONSTANT_Utf8); // Get array of strings containing the argument types String[] args = Utility.methodSignatureArgumentTypes(signature, false); // Get return type string String type = Utility.methodSignatureReturnType(signature, false); String ret_type = Class2HTML.referenceType(type); StringBuffer buf = new StringBuffer("("); for (int i = 0; i < args.length; i++) { buf.append(Class2HTML.referenceType(args[i])); if (i < args.length - 1) buf.append(", "); } buf.append(")"); String arg_types = buf.toString(); if (method_class.equals(class_name)) // Method is local to class ref = "<A HREF=\"" + class_name + "_code.html#method" + getMethodNumber(method_name + signature) + "\" TARGET=Code>" + html_method_name + "</A>"; else ref = "<A HREF=\"" + method_class + ".html" + "\" TARGET=_top>" + short_method_class + "</A>." + html_method_name; constant_ref[index] = ret_type + " <A HREF=\"" + class_name + "_cp.html#cp" + class_index + "\" TARGET=Constants>" + short_method_class + "</A>.<A HREF=\"" + class_name + "_cp.html#cp" + index + "\" TARGET=ConstantPool>" + html_method_name + "</A> " + arg_types; file.println( "<P><TT>" + ret_type + " " + ref + arg_types + " </TT>\n<UL>" + "<LI><A HREF=\"#cp" + class_index + "\">Class index(" + class_index + ")</A>\n" + "<LI><A HREF=\"#cp" + name_index + "\">NameAndType index(" + name_index + ")</A></UL>"); break; case CONSTANT_Fieldref: // Get class_index and name_and_type_index ConstantFieldref c3 = (ConstantFieldref) constant_pool.getConstant(index, CONSTANT_Fieldref); class_index = c3.getClassIndex(); name_index = c3.getNameAndTypeIndex(); // Get method name and its class (compacted) String field_class = constant_pool.constantToString(class_index, CONSTANT_Class); String short_field_class = Utility.compactClassName(field_class); // I.e., remove java.lang. short_field_class = Utility.compactClassName( short_field_class, class_package + ".", true); // Remove class package prefix String field_name = constant_pool.constantToString(name_index, CONSTANT_NameAndType); if (field_class.equals(class_name)) // Field is local to class ref = "<A HREF=\"" + field_class + "_methods.html#field" + field_name + "\" TARGET=Methods>" + field_name + "</A>"; else ref = "<A HREF=\"" + field_class + ".html\" TARGET=_top>" + short_field_class + "</A>." + field_name + "\n"; constant_ref[index] = "<A HREF=\"" + class_name + "_cp.html#cp" + class_index + "\" TARGET=Constants>" + short_field_class + "</A>.<A HREF=\"" + class_name + "_cp.html#cp" + index + "\" TARGET=ConstantPool>" + field_name + "</A>"; file.println( "<P><TT>" + ref + "</TT><BR>\n" + "<UL>" + "<LI><A HREF=\"#cp" + class_index + "\">Class(" + class_index + ")</A><BR>\n" + "<LI><A HREF=\"#cp" + name_index + "\">NameAndType(" + name_index + ")</A></UL>"); break; case CONSTANT_Class: ConstantClass c4 = (ConstantClass) constant_pool.getConstant(index, CONSTANT_Class); name_index = c4.getNameIndex(); String class_name2 = constant_pool.constantToString(index, tag); // / -> . String short_class_name = Utility.compactClassName(class_name2); // I.e., remove java.lang. short_class_name = Utility.compactClassName( short_class_name, class_package + ".", true); // Remove class package prefix ref = "<A HREF=\"" + class_name2 + ".html\" TARGET=_top>" + short_class_name + "</A>"; constant_ref[index] = "<A HREF=\"" + class_name + "_cp.html#cp" + index + "\" TARGET=ConstantPool>" + short_class_name + "</A>"; file.println( "<P><TT>" + ref + "</TT><UL>" + "<LI><A HREF=\"#cp" + name_index + "\">Name index(" + name_index + ")</A></UL>\n"); break; case CONSTANT_String: ConstantString c5 = (ConstantString) constant_pool.getConstant(index, CONSTANT_String); name_index = c5.getStringIndex(); String str = Class2HTML.toHTML(constant_pool.constantToString(index, tag)); file.println( "<P><TT>" + str + "</TT><UL>" + "<LI><A HREF=\"#cp" + name_index + "\">Name index(" + name_index + ")</A></UL>\n"); break; case CONSTANT_NameAndType: ConstantNameAndType c6 = (ConstantNameAndType) constant_pool.getConstant(index, CONSTANT_NameAndType); name_index = c6.getNameIndex(); int signature_index = c6.getSignatureIndex(); file.println( "<P><TT>" + Class2HTML.toHTML(constant_pool.constantToString(index, tag)) + "</TT><UL>" + "<LI><A HREF=\"#cp" + name_index + "\">Name index(" + name_index + ")</A>\n" + "<LI><A HREF=\"#cp" + signature_index + "\">Signature index(" + signature_index + ")</A></UL>\n"); break; default: file.println( "<P><TT>" + Class2HTML.toHTML(constant_pool.constantToString(index, tag)) + "</TT>\n"); } // switch }