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 }