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