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
00009
00010
00011
00012
00013
00014 final class CodeHTML implements de.fub.bytecode.Constants {
00015 private String class_name;
00016 private Method[] methods;
00017 private PrintWriter file;
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
00043
00044
00045
00046
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
00062
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
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
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
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
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
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
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
00131
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
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
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;
00160 }
00161 else
00162 vindex = bytes.readUnsignedByte();
00163
00164 buf.append("%" + vindex);
00165 break;
00166
00167
00168
00169
00170
00171
00172 case WIDE:
00173 wide = true;
00174 buf.append("(wide)");
00175 break;
00176
00177
00178
00179 case NEWARRAY:
00180 buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>");
00181 break;
00182
00183
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)) {
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
00206
00207 case CHECKCAST: case INSTANCEOF: case NEW:
00208 index = bytes.readShort();
00209 buf.append(constant_html.referenceConstant(index));
00210 break;
00211
00212
00213
00214 case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE:
00215 int m_index = bytes.readShort();
00216 String str;
00217
00218 if(opcode == INVOKEINTERFACE) {
00219 int nargs = bytes.readUnsignedByte();
00220 int reserved = bytes.readUnsignedByte();
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
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
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
00258 buf.append("):" + Class2HTML.referenceType(type));
00259
00260 break;
00261
00262
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
00286
00287 case ANEWARRAY:
00288 index = bytes.readShort();
00289
00290 buf.append(constant_html.referenceConstant(index));
00291 break;
00292
00293
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
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:
00325 buf.append(bytes.readShort());
00326 break;
00327
00328 case T_INT:
00329 buf.append(bytes.readInt());
00330 break;
00331
00332 default:
00333 System.err.println("Unreachable default case reached!");
00334 System.exit(-1);
00335 }
00336 buf.append(" ");
00337 }
00338 }
00339 }
00340
00341 buf.append("</TD>");
00342 return buf.toString();
00343 }
00344
00345
00346
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
00356
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
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
00387 for(int i=0; bytes.available() > 0; i++) {
00388 opcode = bytes.readUnsignedByte();
00389
00390 switch(opcode) {
00391 case TABLESWITCH: case LOOKUPSWITCH:
00392
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
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 {
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
00439 index = bytes.getIndex() + bytes.readShort() - 1;
00440
00441 goto_set.set(index);
00442 break;
00443
00444 case GOTO_W: case JSR_W:
00445
00446 index = bytes.getIndex() + bytes.readInt() - 1;
00447 goto_set.set(index);
00448 break;
00449
00450 default:
00451 bytes.unreadByte();
00452 codeToHTML(bytes, 0);
00453 }
00454 }
00455 }
00456
00457
00458
00459 private void writeMethod(Method method, int method_number)
00460 throws IOException
00461 {
00462
00463 String signature = method.getSignature();
00464
00465 String[] args = Utility.methodSignatureArgumentTypes(signature, false);
00466
00467 String type = Utility.methodSignatureReturnType(signature, false);
00468
00469 String name = method.getName();
00470 String html_name = Class2HTML.toHTML(name);
00471
00472 String access = Utility.accessToString(method.getAccessFlags());
00473 access = Utility.replace(access, " ", " ");
00474
00475 Attribute[] attributes= method.getAttributes();
00476
00477 file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " +
00478 "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) +
00479 "</A> <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(", ");
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) {
00524
00525
00526
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
00541
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)
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
00556 file.println("<TR><TD> </A></TD></TR>");
00557 file.println("</TABLE>");
00558 }
00559
00560 }
00561 }