1   package org.apache.bcel.util;
2   
3   /* ====================================================================
4    * The Apache Software License, Version 1.1
5    *
6    * Copyright (c) 2001 The Apache Software Foundation.  All rights
7    * reserved.
8    *
9    * Redistribution and use in source and binary forms, with or without
10   * modification, are permitted provided that the following conditions
11   * are met:
12   *
13   * 1. Redistributions of source code must retain the above copyright
14   *    notice, this list of conditions and the following disclaimer.
15   *
16   * 2. Redistributions in binary form must reproduce the above copyright
17   *    notice, this list of conditions and the following disclaimer in
18   *    the documentation and/or other materials provided with the
19   *    distribution.
20   *
21   * 3. The end-user documentation included with the redistribution,
22   *    if any, must include the following acknowledgment:
23   *       "This product includes software developed by the
24   *        Apache Software Foundation (http://www.apache.org/)."
25   *    Alternately, this acknowledgment may appear in the software itself,
26   *    if and wherever such third-party acknowledgments normally appear.
27   *
28   * 4. The names "Apache" and "Apache Software Foundation" and
29   *    "Apache BCEL" must not be used to endorse or promote products
30   *    derived from this software without prior written permission. For
31   *    written permission, please contact apache@apache.org.
32   *
33   * 5. Products derived from this software may not be called "Apache",
34   *    "Apache BCEL", nor may "Apache" appear in their name, without
35   *    prior written permission of the Apache Software Foundation.
36   *
37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48   * SUCH DAMAGE.
49   * ====================================================================
50   *
51   * This software consists of voluntary contributions made by many
52   * individuals on behalf of the Apache Software Foundation.  For more
53   * information on the Apache Software Foundation, please see
54   * <http://www.apache.org/>.
55   */
56  
57  import org.apache.bcel.classfile.*;
58  import java.io.*;
59  import java.util.BitSet;
60  
61  /***
62   * Convert code into HTML file.
63   *
64   * @version $Id: CodeHTML.java,v 1.2 2002/06/04 11:16:21 mdahm Exp $
65   * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
66   * 
67   */
68  final class CodeHTML implements org.apache.bcel.Constants {
69    private String 	class_name;     // name of current class
70    private Method[]	methods;	// Methods to print
71    private PrintWriter	file;		// file to write to
72    private BitSet    	goto_set;
73    private ConstantPool  constant_pool;
74    private ConstantHTML  constant_html;
75    private static boolean wide=false;
76  
77    CodeHTML(String dir, String class_name,
78  	   Method[] methods, ConstantPool constant_pool,
79  	   ConstantHTML constant_html) throws IOException
80    {
81      this.class_name   	= class_name;
82      this.methods	= methods;
83      this.constant_pool	= constant_pool;
84      this.constant_html = constant_html;
85      
86      file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
87      file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
88      
89      for(int i=0; i < methods.length; i++)
90        writeMethod(methods[i], i);
91  	     
92      file.println("</BODY></HTML>");
93      file.close();
94    }
95                                           
96    /***
97     * Disassemble a stream of byte codes and return the
98     * string representation.
99     *
100    * @param  stream data input stream
101    * @return String representation of byte code
102    */
103   private final String codeToHTML(ByteSequence bytes, int method_number)
104        throws IOException
105   {
106     short        opcode = (short)bytes.readUnsignedByte();
107     StringBuffer buf;
108     String       name, signature;
109     int          default_offset=0, low, high;
110     int          index, class_index, vindex, constant;
111     int[]        jump_table;
112     int          no_pad_bytes=0, offset;
113 
114     buf = new StringBuffer("<TT>" + OPCODE_NAMES[opcode] + "</TT></TD><TD>");
115 
116     /* Special case: Skip (0-3) padding bytes, i.e., the
117      * following bytes are 4-byte-aligned
118      */
119     if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
120       int remainder = bytes.getIndex() % 4;
121       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
122 
123       for(int i=0; i < no_pad_bytes; i++)
124 	bytes.readByte();
125 
126       // Both cases have a field default_offset in common
127       default_offset = bytes.readInt();
128     }
129 
130     switch(opcode) {
131     case TABLESWITCH:
132       low  = bytes.readInt();
133       high = bytes.readInt();
134 
135       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
136       default_offset += offset;
137 
138       buf.append("<TABLE BORDER=1><TR>");
139 
140       // Print switch indices in first row (and default)
141       jump_table = new int[high - low + 1];
142       for(int i=0; i < jump_table.length; i++) {
143 	jump_table[i] = offset + bytes.readInt();
144 
145 	buf.append("<TH>" + (low + i) + "</TH>");
146       }
147       buf.append("<TH>default</TH></TR>\n<TR>");
148 
149       // Print target and default indices in second row
150       for(int i=0; i < jump_table.length; i++)
151 	buf.append("<TD><A HREF=\"#code" + method_number + "@" +
152 		   jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
153       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
154 		 default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
155 
156       break;
157 
158       /* Lookup switch has variable length arguments.
159        */
160     case LOOKUPSWITCH:
161       int npairs = bytes.readInt();
162       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
163       jump_table = new int[npairs];
164       default_offset += offset;
165 
166       buf.append("<TABLE BORDER=1><TR>");
167 
168       // Print switch indices in first row (and default)
169       for(int i=0; i < npairs; i++) {
170 	int match = bytes.readInt();
171 
172 	jump_table[i] = offset + bytes.readInt();
173 	buf.append("<TH>" + match + "</TH>");
174       }
175       buf.append("<TH>default</TH></TR>\n<TR>");
176 
177       // Print target and default indices in second row
178       for(int i=0; i < npairs; i++)
179 	buf.append("<TD><A HREF=\"#code" + method_number + "@" +
180 		   jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
181       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
182 		 default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
183       break;
184 
185       /* Two address bytes + offset from start of byte stream form the
186        * jump target.
187        */
188     case GOTO:      case IFEQ:      case IFGE:      case IFGT:
189     case IFLE:      case IFLT:
190     case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
191     case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
192     case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
193 	  
194       index = (int)(bytes.getIndex() + bytes.readShort() - 1);
195 	  
196       buf.append("<A HREF=\"#code" + method_number + "@" + index + "\">" + index + "</A>");
197       break;
198 	  
199       /* Same for 32-bit wide jumps
200        */
201     case GOTO_W: case JSR_W:
202       int windex = bytes.getIndex() + bytes.readInt() - 1;
203       buf.append("<A HREF=\"#code" + method_number + "@" + windex + "\">" +
204 		 windex + "</A>");
205       break;
206 
207       /* Index byte references local variable (register)
208        */
209     case ALOAD:  case ASTORE: case DLOAD:  case DSTORE: case FLOAD:
210     case FSTORE: case ILOAD:  case ISTORE: case LLOAD:  case LSTORE:
211     case RET: 
212       if(wide) {
213 	vindex = bytes.readShort();
214 	wide=false; // Clear flag
215       }
216       else
217 	vindex = bytes.readUnsignedByte();
218 
219       buf.append("%" + vindex);
220       break;
221 
222       /*
223        * Remember wide byte which is used to form a 16-bit address in the
224        * following instruction. Relies on that the method is called again with
225        * the following opcode.
226        */
227     case WIDE:
228       wide      = true;
229       buf.append("(wide)");
230       break;
231 
232       /* Array of basic type.
233        */
234     case NEWARRAY:
235       buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>");
236       break;
237 
238       /* Access object/class fields.
239        */
240     case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC:
241       index = bytes.readShort();
242       ConstantFieldref c1 = (ConstantFieldref)constant_pool.getConstant(index, CONSTANT_Fieldref);
243 
244       class_index = c1.getClassIndex();
245       name = constant_pool.getConstantString(class_index, CONSTANT_Class);
246       name = Utility.compactClassName(name, false);
247 
248       index = c1.getNameAndTypeIndex();
249       String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType);
250 
251       if(name.equals(class_name)) { // Local field
252 	buf.append("<A HREF=\"" + class_name + "_methods.html#field" + field_name +
253 		   "\" TARGET=Methods>" + field_name + "</A>\n");
254       }
255       else
256 	buf.append(constant_html.referenceConstant(class_index) + "." + field_name);
257 	  		
258       break;
259 	  
260       /* Operands are references to classes in constant pool
261        */
262     case CHECKCAST: case INSTANCEOF: case NEW:
263       index = bytes.readShort();
264       buf.append(constant_html.referenceConstant(index));
265       break;
266    
267       /* Operands are references to methods in constant pool
268        */
269     case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE:
270       int m_index = bytes.readShort();
271       String str;
272 
273       if(opcode == INVOKEINTERFACE) { // Special treatment needed
274 	int nargs    = bytes.readUnsignedByte(); // Redundant
275 	int reserved = bytes.readUnsignedByte(); // Reserved
276 
277 	ConstantInterfaceMethodref c=(ConstantInterfaceMethodref)constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref);
278 
279 	class_index = c.getClassIndex();
280 	str = constant_pool.constantToString(c);
281 	index = c.getNameAndTypeIndex();
282       }
283       else {
284 	ConstantMethodref c = (ConstantMethodref)constant_pool.getConstant(m_index, CONSTANT_Methodref);
285 	class_index = c.getClassIndex();
286 			
287 	str  = constant_pool.constantToString(c);
288 	index = c.getNameAndTypeIndex();
289       }
290 	  		
291       name = Class2HTML.referenceClass(class_index);
292       str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(index, CONSTANT_NameAndType)));
293 
294       // Get signature, i.e., types
295       ConstantNameAndType c2 = (ConstantNameAndType)constant_pool.
296 	getConstant(index, CONSTANT_NameAndType);
297       signature = constant_pool.constantToString(c2.getSignatureIndex(),
298 						 CONSTANT_Utf8);
299       String[] args = Utility.methodSignatureArgumentTypes(signature, false);
300       String   type = Utility.methodSignatureReturnType(signature, false);
301 
302       buf.append(name + ".<A HREF=\"" + class_name + "_cp.html#cp" + m_index + 
303 		 "\" TARGET=ConstantPool>" + str + "</A>" + "(");
304 
305       // List arguments
306       for(int i=0; i < args.length; i++) {
307 	buf.append(Class2HTML.referenceType(args[i]));
308 	  
309 	if(i < args.length - 1)
310 	  buf.append(", ");
311       }
312       // Attach return type
313       buf.append("):" + Class2HTML.referenceType(type));
314 
315       break;
316 		
317       /* Operands are references to items in constant pool
318        */
319     case LDC_W: case LDC2_W:
320       index = bytes.readShort();
321 
322       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 
323 		 "\" TARGET=\"ConstantPool\">" +
324 		 Class2HTML.toHTML(constant_pool.constantToString(index, 
325 								  constant_pool.
326 								  getConstant(index).getTag()))+
327 		 "</a>");
328       break;
329 
330     case LDC:
331       index = bytes.readUnsignedByte();
332       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index + 
333 		 "\" TARGET=\"ConstantPool\">" +
334 		 Class2HTML.toHTML(constant_pool.constantToString(index, 
335 								  constant_pool.
336 								  getConstant(index).getTag()))+
337 		 "</a>");
338       break;
339 	
340       /* Array of references.
341        */
342     case ANEWARRAY:
343       index = bytes.readShort();
344 	  
345       buf.append(constant_html.referenceConstant(index));
346       break;
347 	
348       /* Multidimensional array of references.
349        */
350     case MULTIANEWARRAY:
351       index = bytes.readShort();
352       int dimensions = bytes.readByte();
353       buf.append(constant_html.referenceConstant(index) + ":" + dimensions + "-dimensional");
354       break;
355 
356       /* Increment local variable.
357        */
358     case IINC:
359       if(wide) {
360 	vindex   = bytes.readShort();
361 	constant = bytes.readShort();
362 	wide     = false;
363       }
364       else {
365 	vindex   = bytes.readUnsignedByte();
366 	constant = bytes.readByte();
367       }
368       buf.append("%" + vindex + " " + constant);
369       break;
370 
371     default:
372       if(NO_OF_OPERANDS[opcode] > 0) {
373 	for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
374 	  switch(TYPE_OF_OPERANDS[opcode][i]) {
375 	  case T_BYTE:
376 	    buf.append(bytes.readUnsignedByte());
377 	    break;
378 
379 	  case T_SHORT: // Either branch or index
380 	    buf.append(bytes.readShort());
381 	    break;
382 
383 	  case T_INT:
384 	    buf.append(bytes.readInt());
385 	    break;
386 					      
387 	  default: // Never reached
388 	    System.err.println("Unreachable default case reached!");
389 	    System.exit(-1);
390 	  }
391 	  buf.append(" ");
392 	}
393       }
394     }
395 
396     buf.append("</TD>");
397     return buf.toString();
398   }
399 
400   /***
401    * Find all target addresses in code, so that they can be marked
402    * with <A NAME = ...>. Target addresses are kept in an BitSet object.
403    */
404   private final void findGotos(ByteSequence bytes, Method method, Code code) 
405        throws IOException
406   {
407     int index;
408     goto_set = new BitSet(bytes.available());
409     int opcode;
410 
411     /* First get Code attribute from method and the exceptions handled
412      * (try .. catch) in this method. We only need the line number here.
413      */
414 	
415     if(code != null) {
416       CodeException[] ce  = code.getExceptionTable();
417       int             len = ce.length;
418 
419       for(int i=0; i < len; i++) {
420 	goto_set.set(ce[i].getStartPC());
421 	goto_set.set(ce[i].getEndPC());
422 	goto_set.set(ce[i].getHandlerPC());
423       }
424 
425       // Look for local variables and their range
426       Attribute[] attributes = code.getAttributes();
427       for(int i=0; i < attributes.length; i++) {
428 	if(attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) {
429 	  LocalVariable[] vars = ((LocalVariableTable)attributes[i]).getLocalVariableTable();
430 
431 	  for(int j=0; j < vars.length; j++) {
432 	    int  start = vars[j].getStartPC();
433 	    int  end   = (int)(start + vars[j].getLength());
434 	    goto_set.set(start);
435 	    goto_set.set(end);
436 	  }
437 	  break;
438 	}
439       }
440     }
441 
442     // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
443     for(int i=0; bytes.available() > 0; i++) {
444       opcode = bytes.readUnsignedByte();
445       //System.out.println(OPCODE_NAMES[opcode]);
446       switch(opcode) {
447       case TABLESWITCH: case LOOKUPSWITCH:
448 	//bytes.readByte(); // Skip already read byte
449 
450 	int remainder = bytes.getIndex() % 4;
451 	int no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
452 	int default_offset, offset;
453 
454 	for(int j=0; j < no_pad_bytes; j++)
455 	  bytes.readByte();
456 
457 	// Both cases have a field default_offset in common
458 	default_offset = bytes.readInt();
459 
460 	if(opcode == TABLESWITCH) {
461 	  int low = bytes.readInt();
462 	  int high = bytes.readInt();
463 
464 	  offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
465 	  default_offset += offset;
466 	  goto_set.set(default_offset);
467 
468 	  for(int j=0; j < (high - low + 1); j++) {
469 	    index = offset + bytes.readInt();
470 	    goto_set.set(index);
471 	  }
472 	}
473 	else { // LOOKUPSWITCH
474 	  int npairs = bytes.readInt();
475 
476 	  offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
477 	  default_offset += offset;
478 	  goto_set.set(default_offset);
479 
480 	  for(int j=0; j < npairs; j++) {
481 	    int match = bytes.readInt();
482 
483 	    index = offset + bytes.readInt();
484 	    goto_set.set(index);
485 	  }
486 	}
487 	break;
488 	
489       case GOTO:      case IFEQ:      case IFGE:      case IFGT:
490       case IFLE:      case IFLT:
491       case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
492       case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
493       case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
494 	//bytes.readByte(); // Skip already read byte
495 	index = bytes.getIndex() + bytes.readShort() - 1;
496 	  
497 	goto_set.set(index);
498 	break;
499 
500       case GOTO_W: case JSR_W:
501 	//bytes.readByte(); // Skip already read byte
502 	index = bytes.getIndex() + bytes.readInt() - 1;
503 	goto_set.set(index);
504 	break;
505 
506       default:
507 	bytes.unreadByte();
508 	codeToHTML(bytes, 0); // Ignore output
509       }
510     }
511   }    
512 
513   /***
514    * Write a single method with the byte code associated with it.
515    */
516   private void writeMethod(Method method, int method_number)
517        throws IOException
518   {
519     // Get raw signature
520     String       signature = method.getSignature();
521     // Get array of strings containing the argument types
522     String[]     args      = Utility.methodSignatureArgumentTypes(signature, false);
523     // Get return type string
524     String       type      = Utility.methodSignatureReturnType(signature, false);
525     // Get method name
526     String       name      = method.getName();
527     String    	 html_name = Class2HTML.toHTML(name);
528     // Get method's access flags
529     String       access    = Utility.accessToString(method.getAccessFlags());
530     access = Utility.replace(access, " ", " ");
531     // Get the method's attributes, the Code Attribute in particular
532     Attribute[]  attributes= method.getAttributes();	
533 
534     file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT> " +
535 	       "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) +
536 	       "</A> <A HREF=\"" + class_name + "_methods.html#method" + method_number +
537 	       "\" TARGET=Methods>" + html_name + "</A>(");
538 
539     for(int i=0; i < args.length; i++) {
540       file.print(Class2HTML.referenceType(args[i]));
541       if(i < args.length - 1)
542 	file.print(", ");
543     }
544 
545     file.println(")</B></P>");
546 		
547     Code c=null;
548     byte[] code=null;
549 
550     if(attributes.length > 0) {
551       file.print("<H4>Attributes</H4><UL>\n");
552       for(int i=0; i < attributes.length; i++) {
553 	byte tag = attributes[i].getTag();
554 
555 	if(tag != ATTR_UNKNOWN)
556 	  file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i +
557 		     "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
558 	else
559 	  file.print("<LI>" + attributes[i] + "</LI>");
560 
561 	if(tag == ATTR_CODE) {
562 	  c = (Code)attributes[i];
563 	  Attribute[] attributes2 = c.getAttributes();
564 	  code 								= c.getCode();
565 					
566 	  file.print("<UL>");
567 	  for(int j=0; j < attributes2.length; j++) {
568 	    tag = attributes2[j].getTag();
569 	    file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" +
570 		       "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" +
571 		       ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
572 
573 	  }
574 	  file.print("</UL>");
575 	}
576       }
577       file.println("</UL>");
578     }
579 
580     if(code != null) { // No code, an abstract method, e.g.
581       //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));
582 
583       // Print the byte code
584       ByteSequence stream = new ByteSequence(code);
585       stream.mark(stream.available());
586       findGotos(stream, method, c);
587       stream.reset();
588 
589       file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" +
590 		   "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
591 
592       for(int i=0; stream.available() > 0; i++) {
593 	int offset = stream.getIndex();
594 	String str = codeToHTML(stream, method_number);
595 	String anchor = "";
596 
597 	/* Set an anchor mark if this line is targetted by a goto, jsr, etc.
598 	 * Defining an anchor for every line is very inefficient!
599 	 */
600 	if(goto_set.get(offset))
601 	  anchor = "<A NAME=code" + method_number + "@" + offset +  "></A>";
602 
603 	String anchor2;
604 	if(stream.getIndex() == code.length) // last loop
605 	  anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
606 	else
607 	  anchor2 = "" + offset;
608 
609 	file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
610       }
611       
612       // Mark last line, may be targetted from Attributes window
613       file.println("<TR><TD> </A></TD></TR>");
614       file.println("</TABLE>");
615     }
616 
617   }                          
618 }
This page was automatically generated by Maven