1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.bcel.util;
18
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.PrintWriter;
22 import java.util.BitSet;
23 import org.apache.bcel.classfile.Attribute;
24 import org.apache.bcel.classfile.Code;
25 import org.apache.bcel.classfile.CodeException;
26 import org.apache.bcel.classfile.ConstantFieldref;
27 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
28 import org.apache.bcel.classfile.ConstantMethodref;
29 import org.apache.bcel.classfile.ConstantNameAndType;
30 import org.apache.bcel.classfile.ConstantPool;
31 import org.apache.bcel.classfile.LocalVariable;
32 import org.apache.bcel.classfile.LocalVariableTable;
33 import org.apache.bcel.classfile.Method;
34 import org.apache.bcel.classfile.Utility;
35
36 /***
37 * Convert code into HTML file.
38 *
39 * @version $Id: CodeHTML.java 386056 2006-03-15 11:31:56Z tcurdt $
40 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
41 *
42 */
43 final class CodeHTML implements org.apache.bcel.Constants {
44
45 private String class_name;
46 private Method[] methods;
47 private PrintWriter file;
48 private BitSet goto_set;
49 private ConstantPool constant_pool;
50 private ConstantHTML constant_html;
51 private static boolean wide = false;
52
53
54 CodeHTML(String dir, String class_name, Method[] methods, ConstantPool constant_pool,
55 ConstantHTML constant_html) throws IOException {
56 this.class_name = class_name;
57 this.methods = methods;
58 this.constant_pool = constant_pool;
59 this.constant_html = constant_html;
60 file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
61 file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
62 for (int i = 0; i < methods.length; i++) {
63 writeMethod(methods[i], i);
64 }
65 file.println("</BODY></HTML>");
66 file.close();
67 }
68
69
70 /***
71 * Disassemble a stream of byte codes and return the
72 * string representation.
73 *
74 * @param stream data input stream
75 * @return String representation of byte code
76 */
77 private final String codeToHTML( ByteSequence bytes, int method_number ) throws IOException {
78 short opcode = (short) bytes.readUnsignedByte();
79 StringBuffer buf;
80 String name, signature;
81 int default_offset = 0, low, high;
82 int index, class_index, vindex, constant;
83 int[] jump_table;
84 int no_pad_bytes = 0, offset;
85 buf = new StringBuffer(256);
86 buf.append("<TT>").append(OPCODE_NAMES[opcode]).append("</TT></TD><TD>");
87
88
89
90 if ((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
91 int remainder = bytes.getIndex() % 4;
92 no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
93 for (int i = 0; i < no_pad_bytes; i++) {
94 bytes.readByte();
95 }
96
97 default_offset = bytes.readInt();
98 }
99 switch (opcode) {
100 case TABLESWITCH:
101 low = bytes.readInt();
102 high = bytes.readInt();
103 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
104 default_offset += offset;
105 buf.append("<TABLE BORDER=1><TR>");
106
107 jump_table = new int[high - low + 1];
108 for (int i = 0; i < jump_table.length; i++) {
109 jump_table[i] = offset + bytes.readInt();
110 buf.append("<TH>").append(low + i).append("</TH>");
111 }
112 buf.append("<TH>default</TH></TR>\n<TR>");
113
114 for (int i = 0; i < jump_table.length; i++) {
115 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
116 jump_table[i]).append("\">").append(jump_table[i]).append("</A></TD>");
117 }
118 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
119 default_offset).append("\">").append(default_offset).append(
120 "</A></TD></TR>\n</TABLE>\n");
121 break;
122
123
124 case LOOKUPSWITCH:
125 int npairs = bytes.readInt();
126 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
127 jump_table = new int[npairs];
128 default_offset += offset;
129 buf.append("<TABLE BORDER=1><TR>");
130
131 for (int i = 0; i < npairs; i++) {
132 int match = bytes.readInt();
133 jump_table[i] = offset + bytes.readInt();
134 buf.append("<TH>").append(match).append("</TH>");
135 }
136 buf.append("<TH>default</TH></TR>\n<TR>");
137
138 for (int i = 0; i < npairs; i++) {
139 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
140 jump_table[i]).append("\">").append(jump_table[i]).append("</A></TD>");
141 }
142 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
143 default_offset).append("\">").append(default_offset).append(
144 "</A></TD></TR>\n</TABLE>\n");
145 break;
146
147
148
149 case GOTO:
150 case IFEQ:
151 case IFGE:
152 case IFGT:
153 case IFLE:
154 case IFLT:
155 case IFNE:
156 case IFNONNULL:
157 case IFNULL:
158 case IF_ACMPEQ:
159 case IF_ACMPNE:
160 case IF_ICMPEQ:
161 case IF_ICMPGE:
162 case IF_ICMPGT:
163 case IF_ICMPLE:
164 case IF_ICMPLT:
165 case IF_ICMPNE:
166 case JSR:
167 index = (int) (bytes.getIndex() + bytes.readShort() - 1);
168 buf.append("<A HREF=\"#code").append(method_number).append("@").append(index)
169 .append("\">").append(index).append("</A>");
170 break;
171
172
173 case GOTO_W:
174 case JSR_W:
175 int windex = bytes.getIndex() + bytes.readInt() - 1;
176 buf.append("<A HREF=\"#code").append(method_number).append("@").append(windex)
177 .append("\">").append(windex).append("</A>");
178 break;
179
180
181 case ALOAD:
182 case ASTORE:
183 case DLOAD:
184 case DSTORE:
185 case FLOAD:
186 case FSTORE:
187 case ILOAD:
188 case ISTORE:
189 case LLOAD:
190 case LSTORE:
191 case RET:
192 if (wide) {
193 vindex = bytes.readShort();
194 wide = false;
195 } else {
196 vindex = bytes.readUnsignedByte();
197 }
198 buf.append("%").append(vindex);
199 break;
200
201
202
203
204
205 case WIDE:
206 wide = true;
207 buf.append("(wide)");
208 break;
209
210
211 case NEWARRAY:
212 buf.append("<FONT COLOR=\"#00FF00\">").append(TYPE_NAMES[bytes.readByte()]).append(
213 "</FONT>");
214 break;
215
216
217 case GETFIELD:
218 case GETSTATIC:
219 case PUTFIELD:
220 case PUTSTATIC:
221 index = bytes.readShort();
222 ConstantFieldref c1 = (ConstantFieldref) constant_pool.getConstant(index,
223 CONSTANT_Fieldref);
224 class_index = c1.getClassIndex();
225 name = constant_pool.getConstantString(class_index, CONSTANT_Class);
226 name = Utility.compactClassName(name, false);
227 index = c1.getNameAndTypeIndex();
228 String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType);
229 if (name.equals(class_name)) {
230 buf.append("<A HREF=\"").append(class_name).append("_methods.html#field")
231 .append(field_name).append("\" TARGET=Methods>").append(field_name)
232 .append("</A>\n");
233 } else {
234 buf.append(constant_html.referenceConstant(class_index)).append(".").append(
235 field_name);
236 }
237 break;
238
239
240 case CHECKCAST:
241 case INSTANCEOF:
242 case NEW:
243 index = bytes.readShort();
244 buf.append(constant_html.referenceConstant(index));
245 break;
246
247
248 case INVOKESPECIAL:
249 case INVOKESTATIC:
250 case INVOKEVIRTUAL:
251 case INVOKEINTERFACE:
252 int m_index = bytes.readShort();
253 String str;
254 if (opcode == INVOKEINTERFACE) {
255 int nargs = bytes.readUnsignedByte();
256 int reserved = bytes.readUnsignedByte();
257 ConstantInterfaceMethodref c = (ConstantInterfaceMethodref) constant_pool
258 .getConstant(m_index, CONSTANT_InterfaceMethodref);
259 class_index = c.getClassIndex();
260 str = constant_pool.constantToString(c);
261 index = c.getNameAndTypeIndex();
262 } else {
263 ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(m_index,
264 CONSTANT_Methodref);
265 class_index = c.getClassIndex();
266 str = constant_pool.constantToString(c);
267 index = c.getNameAndTypeIndex();
268 }
269 name = Class2HTML.referenceClass(class_index);
270 str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(
271 index, CONSTANT_NameAndType)));
272
273 ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(index,
274 CONSTANT_NameAndType);
275 signature = constant_pool.constantToString(c2.getSignatureIndex(), CONSTANT_Utf8);
276 String[] args = Utility.methodSignatureArgumentTypes(signature, false);
277 String type = Utility.methodSignatureReturnType(signature, false);
278 buf.append(name).append(".<A HREF=\"").append(class_name).append("_cp.html#cp")
279 .append(m_index).append("\" TARGET=ConstantPool>").append(str).append(
280 "</A>").append("(");
281
282 for (int i = 0; i < args.length; i++) {
283 buf.append(Class2HTML.referenceType(args[i]));
284 if (i < args.length - 1) {
285 buf.append(", ");
286 }
287 }
288
289 buf.append("):").append(Class2HTML.referenceType(type));
290 break;
291
292
293 case LDC_W:
294 case LDC2_W:
295 index = bytes.readShort();
296 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
297 .append("\" TARGET=\"ConstantPool\">").append(
298 Class2HTML.toHTML(constant_pool.constantToString(index,
299 constant_pool.getConstant(index).getTag()))).append("</a>");
300 break;
301 case LDC:
302 index = bytes.readUnsignedByte();
303 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
304 .append("\" TARGET=\"ConstantPool\">").append(
305 Class2HTML.toHTML(constant_pool.constantToString(index,
306 constant_pool.getConstant(index).getTag()))).append("</a>");
307 break;
308
309
310 case ANEWARRAY:
311 index = bytes.readShort();
312 buf.append(constant_html.referenceConstant(index));
313 break;
314
315
316 case MULTIANEWARRAY:
317 index = bytes.readShort();
318 int dimensions = bytes.readByte();
319 buf.append(constant_html.referenceConstant(index)).append(":").append(dimensions)
320 .append("-dimensional");
321 break;
322
323
324 case IINC:
325 if (wide) {
326 vindex = bytes.readShort();
327 constant = bytes.readShort();
328 wide = false;
329 } else {
330 vindex = bytes.readUnsignedByte();
331 constant = bytes.readByte();
332 }
333 buf.append("%").append(vindex).append(" ").append(constant);
334 break;
335 default:
336 if (NO_OF_OPERANDS[opcode] > 0) {
337 for (int i = 0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
338 switch (TYPE_OF_OPERANDS[opcode][i]) {
339 case T_BYTE:
340 buf.append(bytes.readUnsignedByte());
341 break;
342 case T_SHORT:
343 buf.append(bytes.readShort());
344 break;
345 case T_INT:
346 buf.append(bytes.readInt());
347 break;
348 default:
349 System.err.println("Unreachable default case reached!");
350 System.exit(-1);
351 }
352 buf.append(" ");
353 }
354 }
355 }
356 buf.append("</TD>");
357 return buf.toString();
358 }
359
360
361 /***
362 * Find all target addresses in code, so that they can be marked
363 * with <A NAME = ...>. Target addresses are kept in an BitSet object.
364 */
365 private final void findGotos( ByteSequence bytes, Method method, Code code ) throws IOException {
366 int index;
367 goto_set = new BitSet(bytes.available());
368 int opcode;
369
370
371
372 if (code != null) {
373 CodeException[] ce = code.getExceptionTable();
374 int len = ce.length;
375 for (int i = 0; i < len; i++) {
376 goto_set.set(ce[i].getStartPC());
377 goto_set.set(ce[i].getEndPC());
378 goto_set.set(ce[i].getHandlerPC());
379 }
380
381 Attribute[] attributes = code.getAttributes();
382 for (int i = 0; i < attributes.length; i++) {
383 if (attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) {
384 LocalVariable[] vars = ((LocalVariableTable) attributes[i])
385 .getLocalVariableTable();
386 for (int j = 0; j < vars.length; j++) {
387 int start = vars[j].getStartPC();
388 int end = (int) (start + vars[j].getLength());
389 goto_set.set(start);
390 goto_set.set(end);
391 }
392 break;
393 }
394 }
395 }
396
397 for (int i = 0; bytes.available() > 0; i++) {
398 opcode = bytes.readUnsignedByte();
399
400 switch (opcode) {
401 case TABLESWITCH:
402 case LOOKUPSWITCH:
403
404 int remainder = bytes.getIndex() % 4;
405 int no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
406 int default_offset,
407 offset;
408 for (int j = 0; j < no_pad_bytes; j++) {
409 bytes.readByte();
410 }
411
412 default_offset = bytes.readInt();
413 if (opcode == TABLESWITCH) {
414 int low = bytes.readInt();
415 int high = bytes.readInt();
416 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
417 default_offset += offset;
418 goto_set.set(default_offset);
419 for (int j = 0; j < (high - low + 1); j++) {
420 index = offset + bytes.readInt();
421 goto_set.set(index);
422 }
423 } else {
424 int npairs = bytes.readInt();
425 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
426 default_offset += offset;
427 goto_set.set(default_offset);
428 for (int j = 0; j < npairs; j++) {
429 int match = bytes.readInt();
430 index = offset + bytes.readInt();
431 goto_set.set(index);
432 }
433 }
434 break;
435 case GOTO:
436 case IFEQ:
437 case IFGE:
438 case IFGT:
439 case IFLE:
440 case IFLT:
441 case IFNE:
442 case IFNONNULL:
443 case IFNULL:
444 case IF_ACMPEQ:
445 case IF_ACMPNE:
446 case IF_ICMPEQ:
447 case IF_ICMPGE:
448 case IF_ICMPGT:
449 case IF_ICMPLE:
450 case IF_ICMPLT:
451 case IF_ICMPNE:
452 case JSR:
453
454 index = bytes.getIndex() + bytes.readShort() - 1;
455 goto_set.set(index);
456 break;
457 case GOTO_W:
458 case JSR_W:
459
460 index = bytes.getIndex() + bytes.readInt() - 1;
461 goto_set.set(index);
462 break;
463 default:
464 bytes.unreadByte();
465 codeToHTML(bytes, 0);
466 }
467 }
468 }
469
470
471 /***
472 * Write a single method with the byte code associated with it.
473 */
474 private void writeMethod( Method method, int method_number ) throws IOException {
475
476 String signature = method.getSignature();
477
478 String[] args = Utility.methodSignatureArgumentTypes(signature, false);
479
480 String type = Utility.methodSignatureReturnType(signature, false);
481
482 String name = method.getName();
483 String html_name = Class2HTML.toHTML(name);
484
485 String access = Utility.accessToString(method.getAccessFlags());
486 access = Utility.replace(access, " ", " ");
487
488 Attribute[] attributes = method.getAttributes();
489 file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " + "<A NAME=method"
490 + method_number + ">" + Class2HTML.referenceType(type) + "</A> <A HREF=\""
491 + class_name + "_methods.html#method" + method_number + "\" TARGET=Methods>"
492 + html_name + "</A>(");
493 for (int i = 0; i < args.length; i++) {
494 file.print(Class2HTML.referenceType(args[i]));
495 if (i < args.length - 1) {
496 file.print(", ");
497 }
498 }
499 file.println(")</B></P>");
500 Code c = null;
501 byte[] code = null;
502 if (attributes.length > 0) {
503 file.print("<H4>Attributes</H4><UL>\n");
504 for (int i = 0; i < attributes.length; i++) {
505 byte tag = attributes[i].getTag();
506 if (tag != ATTR_UNKNOWN) {
507 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method"
508 + method_number + "@" + i + "\" TARGET=Attributes>"
509 + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
510 } else {
511 file.print("<LI>" + attributes[i] + "</LI>");
512 }
513 if (tag == ATTR_CODE) {
514 c = (Code) attributes[i];
515 Attribute[] attributes2 = c.getAttributes();
516 code = c.getCode();
517 file.print("<UL>");
518 for (int j = 0; j < attributes2.length; j++) {
519 tag = attributes2[j].getTag();
520 file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" + "method"
521 + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>"
522 + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
523 }
524 file.print("</UL>");
525 }
526 }
527 file.println("</UL>");
528 }
529 if (code != null) {
530
531
532 ByteSequence stream = new ByteSequence(code);
533 stream.mark(stream.available());
534 findGotos(stream, method, c);
535 stream.reset();
536 file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>"
537 + "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
538 for (int i = 0; stream.available() > 0; i++) {
539 int offset = stream.getIndex();
540 String str = codeToHTML(stream, method_number);
541 String anchor = "";
542
543
544
545 if (goto_set.get(offset)) {
546 anchor = "<A NAME=code" + method_number + "@" + offset + "></A>";
547 }
548 String anchor2;
549 if (stream.getIndex() == code.length) {
550 anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset
551 + "</A>";
552 } else {
553 anchor2 = "" + offset;
554 }
555 file
556 .println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str
557 + "</TR>");
558 }
559
560 file.println("<TR><TD> </A></TD></TR>");
561 file.println("</TABLE>");
562 }
563 }
564 }