< prev index next >

src/java.xml/share/classes/com/sun/org/apache/bcel/internal/util/CodeHTML.java

Print this page




   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.bcel.internal.util;
  23 
  24 
  25 import com.sun.org.apache.bcel.internal.classfile.*;
  26 import java.io.*;
  27 import java.util.BitSet;
  28 















  29 /**
  30  * Convert code into HTML file.
  31  *
  32  * @author  <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  33  *
  34  */
  35 final class CodeHTML implements com.sun.org.apache.bcel.internal.Constants {
  36   private String        class_name;     // name of current class
  37   private Method[]      methods;        // Methods to print
  38   private PrintWriter   file;           // file to write to

  39   private BitSet        goto_set;
  40   private ConstantPool  constant_pool;
  41   private ConstantHTML  constant_html;
  42   private static boolean wide=false;
  43 
  44   CodeHTML(String dir, String class_name,
  45            Method[] methods, ConstantPool constant_pool,
  46            ConstantHTML constant_html) throws IOException
  47   {
  48     this.class_name     = class_name;
  49     this.methods        = methods;
  50     this.constant_pool  = constant_pool;
  51     this.constant_html = constant_html;
  52 
  53     file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
  54     file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
  55 
  56     for(int i=0; i < methods.length; i++)
  57       writeMethod(methods[i], i);
  58 
  59     file.println("</BODY></HTML>");
  60     file.close();
  61   }
  62 

  63   /**
  64    * Disassemble a stream of byte codes and return the
  65    * string representation.
  66    *
  67    * @param  stream data input stream
  68    * @return String representation of byte code
  69    */
  70   private final String codeToHTML(ByteSequence bytes, int method_number)
  71        throws IOException
  72   {
  73     short        opcode = (short)bytes.readUnsignedByte();
  74     StringBuffer buf;
  75     String       name, signature;
  76     int          default_offset=0, low, high;
  77     int          index, class_index, vindex, constant;



  78     int[]        jump_table;
  79     int          no_pad_bytes=0, offset;
  80 
  81     buf = new StringBuffer("<TT>" + OPCODE_NAMES[opcode] + "</TT></TD><TD>");
  82 
  83     /* Special case: Skip (0-3) padding bytes, i.e., the
  84      * following bytes are 4-byte-aligned
  85      */
  86     if((opcode == TABLESWITCH) || (opcode == LOOKUPSWITCH)) {
  87       int remainder = bytes.getIndex() % 4;
  88       no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
  89 
  90       for(int i=0; i < no_pad_bytes; i++)
  91         bytes.readByte();
  92 
  93       // Both cases have a field default_offset in common
  94       default_offset = bytes.readInt();
  95     }
  96 
  97     switch(opcode) {
  98     case TABLESWITCH:
  99       low  = bytes.readInt();
 100       high = bytes.readInt();
 101 
 102       offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 103       default_offset += offset;
 104 
 105       buf.append("<TABLE BORDER=1><TR>");
 106 
 107       // Print switch indices in first row (and default)
 108       jump_table = new int[high - low + 1];
 109       for(int i=0; i < jump_table.length; i++) {
 110         jump_table[i] = offset + bytes.readInt();
 111 
 112         buf.append("<TH>" + (low + i) + "</TH>");
 113       }
 114       buf.append("<TH>default</TH></TR>\n<TR>");
 115 
 116       // Print target and default indices in second row
 117       for(int i=0; i < jump_table.length; i++)
 118         buf.append("<TD><A HREF=\"#code" + method_number + "@" +
 119                    jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
 120       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
 121                  default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");
 122 

 123       break;
 124 
 125       /* Lookup switch has variable length arguments.
 126        */
 127     case LOOKUPSWITCH:
 128       int npairs = bytes.readInt();
 129       offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 130       jump_table = new int[npairs];
 131       default_offset += offset;
 132 
 133       buf.append("<TABLE BORDER=1><TR>");
 134 
 135       // Print switch indices in first row (and default)
 136       for(int i=0; i < npairs; i++) {
 137         int match = bytes.readInt();
 138 
 139         jump_table[i] = offset + bytes.readInt();
 140         buf.append("<TH>" + match + "</TH>");
 141       }
 142       buf.append("<TH>default</TH></TR>\n<TR>");
 143 
 144       // Print target and default indices in second row
 145       for(int i=0; i < npairs; i++)
 146         buf.append("<TD><A HREF=\"#code" + method_number + "@" +
 147                    jump_table[i] + "\">" + jump_table[i] + "</A></TD>");
 148       buf.append("<TD><A HREF=\"#code" + method_number + "@" +
 149                  default_offset + "\">" + default_offset + "</A></TD></TR>\n</TABLE>\n");


 150       break;
 151 
 152       /* Two address bytes + offset from start of byte stream form the
 153        * jump target.
 154        */
 155     case GOTO:      case IFEQ:      case IFGE:      case IFGT:
 156     case IFLE:      case IFLT:
 157     case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
 158     case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
 159     case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:
 160 
 161       index = (int)(bytes.getIndex() + bytes.readShort() - 1);
 162 
 163       buf.append("<A HREF=\"#code" + method_number + "@" + index + "\">" + index + "</A>");












 164       break;
 165 
 166       /* Same for 32-bit wide jumps
 167        */
 168     case GOTO_W: case JSR_W:
 169       int windex = bytes.getIndex() + bytes.readInt() - 1;
 170       buf.append("<A HREF=\"#code" + method_number + "@" + windex + "\">" +
 171                  windex + "</A>");

 172       break;
 173 
 174       /* Index byte references local variable (register)
 175        */
 176     case ALOAD:  case ASTORE: case DLOAD:  case DSTORE: case FLOAD:
 177     case FSTORE: case ILOAD:  case ISTORE: case LLOAD:  case LSTORE:
 178     case RET:
 179       if(wide) {








 180         vindex = bytes.readShort();
 181         wide=false; // Clear flag
 182       }
 183       else
 184         vindex = bytes.readUnsignedByte();
 185 
 186       buf.append("%" + vindex);
 187       break;
 188 
 189       /*
 190        * Remember wide byte which is used to form a 16-bit address in the
 191        * following instruction. Relies on that the method is called again with
 192        * the following opcode.
 193        */
 194     case WIDE:
 195       wide      = true;
 196       buf.append("(wide)");
 197       break;
 198 
 199       /* Array of basic type.
 200        */
 201     case NEWARRAY:
 202       buf.append("<FONT COLOR=\"#00FF00\">" + TYPE_NAMES[bytes.readByte()] + "</FONT>");

 203       break;
 204 
 205       /* Access object/class fields.
 206        */
 207     case GETFIELD: case GETSTATIC: case PUTFIELD: case PUTSTATIC:



 208       index = bytes.readShort();
 209       ConstantFieldref c1 = (ConstantFieldref)constant_pool.getConstant(index, CONSTANT_Fieldref);
 210 
 211       class_index = c1.getClassIndex();
 212       name = constant_pool.getConstantString(class_index, CONSTANT_Class);
 213       name = Utility.compactClassName(name, false);
 214 
 215       index = c1.getNameAndTypeIndex();
 216       String field_name = constant_pool.constantToString(index, CONSTANT_NameAndType);
 217 
 218       if(name.equals(class_name)) { // Local field
 219         buf.append("<A HREF=\"" + class_name + "_methods.html#field" + field_name +
 220                    "\" TARGET=Methods>" + field_name + "</A>\n");



 221       }
 222       else
 223         buf.append(constant_html.referenceConstant(class_index) + "." + field_name);
 224 
 225       break;
 226 
 227       /* Operands are references to classes in constant pool
 228        */
 229     case CHECKCAST: case INSTANCEOF: case NEW:


 230       index = bytes.readShort();
 231       buf.append(constant_html.referenceConstant(index));
 232       break;
 233 
 234       /* Operands are references to methods in constant pool
 235        */
 236     case INVOKESPECIAL: case INVOKESTATIC: case INVOKEVIRTUAL: case INVOKEINTERFACE:
 237       int m_index = bytes.readShort();




 238       String str;
 239 
 240       if(opcode == INVOKEINTERFACE) { // Special treatment needed
 241         int nargs    = bytes.readUnsignedByte(); // Redundant
 242         int reserved = bytes.readUnsignedByte(); // Reserved
 243 
 244         ConstantInterfaceMethodref c=(ConstantInterfaceMethodref)constant_pool.getConstant(m_index, CONSTANT_InterfaceMethodref);
 245 
 246         class_index = c.getClassIndex();
 247         str = constant_pool.constantToString(c);
 248         index = c.getNameAndTypeIndex();
 249       }
 250       else {
 251         ConstantMethodref c = (ConstantMethodref)constant_pool.getConstant(m_index, CONSTANT_Methodref);











 252         class_index = c.getClassIndex();
 253 
 254         str  = constant_pool.constantToString(c);
 255         index = c.getNameAndTypeIndex();
 256       }
 257 
 258       name = Class2HTML.referenceClass(class_index);
 259       str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(index, CONSTANT_NameAndType)));
 260 

 261       // Get signature, i.e., types
 262       ConstantNameAndType c2 = (ConstantNameAndType)constant_pool.
 263         getConstant(index, CONSTANT_NameAndType);
 264       signature = constant_pool.constantToString(c2.getSignatureIndex(),
 265                                                  CONSTANT_Utf8);
 266       String[] args = Utility.methodSignatureArgumentTypes(signature, false);
 267       String   type = Utility.methodSignatureReturnType(signature, false);
 268 
 269       buf.append(name + ".<A HREF=\"" + class_name + "_cp.html#cp" + m_index +
 270                  "\" TARGET=ConstantPool>" + str + "</A>" + "(");
 271 
 272       // List arguments
 273       for(int i=0; i < args.length; i++) {
 274         buf.append(Class2HTML.referenceType(args[i]));
 275 
 276         if(i < args.length - 1)
 277           buf.append(", ");
 278       }

 279       // Attach return type
 280       buf.append("):" + Class2HTML.referenceType(type));
 281 
 282       break;
 283 
 284       /* Operands are references to items in constant pool
 285        */
 286     case LDC_W: case LDC2_W:

 287       index = bytes.readShort();
 288 
 289       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index +
 290                  "\" TARGET=\"ConstantPool\">" +
 291                  Class2HTML.toHTML(constant_pool.constantToString(index,
 292                                                                   constant_pool.
 293                                                                   getConstant(index).getTag()))+
 294                  "</a>");
 295       break;
 296 
 297     case LDC:
 298       index = bytes.readUnsignedByte();
 299       buf.append("<A HREF=\"" + class_name + "_cp.html#cp" + index +
 300                  "\" TARGET=\"ConstantPool\">" +
 301                  Class2HTML.toHTML(constant_pool.constantToString(index,
 302                                                                   constant_pool.
 303                                                                   getConstant(index).getTag()))+
 304                  "</a>");
 305       break;
 306 
 307       /* Array of references.
 308        */
 309     case ANEWARRAY:
 310       index = bytes.readShort();
 311 
 312       buf.append(constant_html.referenceConstant(index));
 313       break;
 314 
 315       /* Multidimensional array of references.
 316        */
 317     case MULTIANEWARRAY:
 318       index = bytes.readShort();
 319       int dimensions = bytes.readByte();
 320       buf.append(constant_html.referenceConstant(index) + ":" + dimensions + "-dimensional");

 321       break;
 322 
 323       /* Increment local variable.
 324        */
 325     case IINC:
 326       if(wide) {
 327         vindex   = bytes.readShort();
 328         constant = bytes.readShort();
 329         wide     = false;
 330       }
 331       else {
 332         vindex   = bytes.readUnsignedByte();
 333         constant = bytes.readByte();
 334       }
 335       buf.append("%" + vindex + " " + constant);
 336       break;
 337 
 338     default:
 339       if(NO_OF_OPERANDS[opcode] > 0) {
 340         for(int i=0; i < TYPE_OF_OPERANDS[opcode].length; i++) {
 341           switch(TYPE_OF_OPERANDS[opcode][i]) {
 342           case T_BYTE:
 343             buf.append(bytes.readUnsignedByte());
 344             break;
 345 
 346           case T_SHORT: // Either branch or index
 347             buf.append(bytes.readShort());
 348             break;
 349 
 350           case T_INT:
 351             buf.append(bytes.readInt());
 352             break;
 353 
 354           default: // Never reached
 355             System.err.println("Unreachable default case reached!");
 356             System.exit(-1);

 357           }
 358           buf.append("&nbsp;");
 359         }
 360       }
 361     }
 362 
 363     buf.append("</TD>");
 364     return buf.toString();
 365   }
 366 

 367   /**
 368    * Find all target addresses in code, so that they can be marked
 369    * with &lt;A NAME = ...&gt;. Target addresses are kept in an BitSet object.
 370    */
 371   private final void findGotos(ByteSequence bytes, Method method, Code code)
 372        throws IOException
 373   {
 374     int index;
 375     goto_set = new BitSet(bytes.available());
 376     int opcode;
 377 
 378     /* First get Code attribute from method and the exceptions handled
 379      * (try .. catch) in this method. We only need the line number here.
 380      */
 381 
 382     if(code != null) {
 383       CodeException[] ce  = code.getExceptionTable();
 384       int             len = ce.length;
 385 
 386       for(int i=0; i < len; i++) {
 387         goto_set.set(ce[i].getStartPC());
 388         goto_set.set(ce[i].getEndPC());
 389         goto_set.set(ce[i].getHandlerPC());
 390       }
 391 
 392       // Look for local variables and their range
 393       Attribute[] attributes = code.getAttributes();
 394       for(int i=0; i < attributes.length; i++) {
 395         if(attributes[i].getTag() == ATTR_LOCAL_VARIABLE_TABLE) {
 396           LocalVariable[] vars = ((LocalVariableTable)attributes[i]).getLocalVariableTable();
 397 
 398           for(int j=0; j < vars.length; j++) {
 399             int  start = vars[j].getStartPC();
 400             int  end   = (int)(start + vars[j].getLength());
 401             goto_set.set(start);
 402             goto_set.set(end);
 403           }
 404           break;
 405         }
 406       }
 407     }
 408 
 409     // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
 410     for(int i=0; bytes.available() > 0; i++) {
 411       opcode = bytes.readUnsignedByte();
 412       //System.out.println(OPCODE_NAMES[opcode]);
 413       switch(opcode) {
 414       case TABLESWITCH: case LOOKUPSWITCH:

 415         //bytes.readByte(); // Skip already read byte
 416 
 417         int remainder = bytes.getIndex() % 4;
 418         int no_pad_bytes  = (remainder == 0)? 0 : 4 - remainder;
 419         int default_offset, offset;
 420 
 421         for(int j=0; j < no_pad_bytes; j++)
 422           bytes.readByte();
 423 
 424         // Both cases have a field default_offset in common
 425         default_offset = bytes.readInt();
 426 
 427         if(opcode == TABLESWITCH) {
 428           int low = bytes.readInt();
 429           int high = bytes.readInt();
 430 
 431           offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 432           default_offset += offset;
 433           goto_set.set(default_offset);
 434 
 435           for(int j=0; j < (high - low + 1); j++) {
 436             index = offset + bytes.readInt();
 437             goto_set.set(index);
 438           }
 439         }
 440         else { // LOOKUPSWITCH
 441           int npairs = bytes.readInt();
 442 
 443           offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 444           default_offset += offset;
 445           goto_set.set(default_offset);
 446 
 447           for(int j=0; j < npairs; j++) {
 448             int match = bytes.readInt();
 449 
 450             index = offset + bytes.readInt();
 451             goto_set.set(index);
 452           }
 453         }
 454         break;
 455 
 456       case GOTO:      case IFEQ:      case IFGE:      case IFGT:
 457       case IFLE:      case IFLT:
 458       case IFNE:      case IFNONNULL: case IFNULL:    case IF_ACMPEQ:
 459       case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT:
 460       case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: case JSR:












 461         //bytes.readByte(); // Skip already read byte
 462         index = bytes.getIndex() + bytes.readShort() - 1;
 463 
 464         goto_set.set(index);
 465         break;
 466 
 467       case GOTO_W: case JSR_W:
 468         //bytes.readByte(); // Skip already read byte
 469         index = bytes.getIndex() + bytes.readInt() - 1;
 470         goto_set.set(index);
 471         break;
 472 
 473       default:
 474         bytes.unreadByte();
 475         codeToHTML(bytes, 0); // Ignore output
 476       }
 477     }
 478   }
 479 

 480   /**
 481    * Write a single method with the byte code associated with it.
 482    */
 483   private void writeMethod(Method method, int method_number)
 484        throws IOException
 485   {
 486     // Get raw signature
 487     String       signature = method.getSignature();
 488     // Get array of strings containing the argument types
 489     String[]     args      = Utility.methodSignatureArgumentTypes(signature, false);
 490     // Get return type string
 491     String       type      = Utility.methodSignatureReturnType(signature, false);
 492     // Get method name
 493     String       name      = method.getName();
 494     String       html_name = Class2HTML.toHTML(name);
 495     // Get method's access flags
 496     String       access    = Utility.accessToString(method.getAccessFlags());
 497     access = Utility.replace(access, " ", "&nbsp;");
 498     // Get the method's attributes, the Code Attribute in particular
 499     Attribute[]  attributes= method.getAttributes();
 500 
 501     file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT>&nbsp;" +
 502                "<A NAME=method" + method_number + ">" + Class2HTML.referenceType(type) +
 503                "</A>&nbsp<A HREF=\"" + class_name + "_methods.html#method" + method_number +
 504                "\" TARGET=Methods>" + html_name + "</A>(");
 505 
 506     for(int i=0; i < args.length; i++) {
 507       file.print(Class2HTML.referenceType(args[i]));
 508       if(i < args.length - 1)
 509         file.print(",&nbsp;");
 510     }
 511 
 512     file.println(")</B></P>");
 513 
 514     Code c=null;
 515     byte[] code=null;
 516 
 517     if(attributes.length > 0) {
 518       file.print("<H4>Attributes</H4><UL>\n");
 519       for(int i=0; i < attributes.length; i++) {
 520         byte tag = attributes[i].getTag();
 521 
 522         if(tag != ATTR_UNKNOWN)
 523           file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method" + method_number + "@" + i +
 524                      "\" TARGET=Attributes>" + ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
 525         else
 526           file.print("<LI>" + attributes[i] + "</LI>");
 527 
 528         if(tag == ATTR_CODE) {
 529           c = (Code)attributes[i];
 530           Attribute[] attributes2 = c.getAttributes();
 531           code                                                          = c.getCode();
 532 
 533           file.print("<UL>");
 534           for(int j=0; j < attributes2.length; j++) {
 535             tag = attributes2[j].getTag();
 536             file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" +
 537                        "method" + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>" +
 538                        ATTRIBUTE_NAMES[tag] + "</A></LI>\n");
 539 
 540           }
 541           file.print("</UL>");
 542         }
 543       }
 544       file.println("</UL>");
 545     }
 546 
 547     if(code != null) { // No code, an abstract method, e.g.
 548       //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));
 549 
 550       // Print the byte code
 551       ByteSequence stream = new ByteSequence(code);
 552       stream.mark(stream.available());
 553       findGotos(stream, method, c);
 554       stream.reset();
 555 
 556       file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>" +
 557                    "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
 558 
 559       for(int i=0; stream.available() > 0; i++) {
 560         int offset = stream.getIndex();
 561         String str = codeToHTML(stream, method_number);
 562         String anchor = "";
 563 
 564         /* Set an anchor mark if this line is targetted by a goto, jsr, etc.
 565          * Defining an anchor for every line is very inefficient!
 566          */
 567         if(goto_set.get(offset))
 568           anchor = "<A NAME=code" + method_number + "@" + offset +  "></A>";
 569 
 570         String anchor2;
 571         if(stream.getIndex() == code.length) // last loop
 572           anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
 573         else
 574           anchor2 = "" + offset;
 575 
 576         file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
 577       }
 578 
 579       // Mark last line, may be targetted from Attributes window
 580       file.println("<TR><TD> </A></TD></TR>");
 581       file.println("</TABLE>");
 582     }
 583 
 584   }
 585 }


   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.bcel.internal.util;
  23 
  24 import java.io.FileOutputStream;
  25 import java.io.IOException;
  26 import java.io.PrintWriter;
  27 import java.util.BitSet;
  28 
  29 import com.sun.org.apache.bcel.internal.Const;
  30 import com.sun.org.apache.bcel.internal.classfile.Attribute;
  31 import com.sun.org.apache.bcel.internal.classfile.Code;
  32 import com.sun.org.apache.bcel.internal.classfile.CodeException;
  33 import com.sun.org.apache.bcel.internal.classfile.ConstantFieldref;
  34 import com.sun.org.apache.bcel.internal.classfile.ConstantInterfaceMethodref;
  35 import com.sun.org.apache.bcel.internal.classfile.ConstantInvokeDynamic;
  36 import com.sun.org.apache.bcel.internal.classfile.ConstantMethodref;
  37 import com.sun.org.apache.bcel.internal.classfile.ConstantNameAndType;
  38 import com.sun.org.apache.bcel.internal.classfile.ConstantPool;
  39 import com.sun.org.apache.bcel.internal.classfile.LocalVariable;
  40 import com.sun.org.apache.bcel.internal.classfile.LocalVariableTable;
  41 import com.sun.org.apache.bcel.internal.classfile.Method;
  42 import com.sun.org.apache.bcel.internal.classfile.Utility;
  43 
  44 /**
  45  * Convert code into HTML file.
  46  *
  47  * @version $Id: CodeHTML.java 1749603 2016-06-21 20:50:19Z ggregory $
  48  *
  49  */
  50 final class CodeHTML {
  51 
  52     private final String class_name; // name of current class
  53 //    private Method[] methods; // Methods to print
  54     private final PrintWriter file; // file to write to
  55     private BitSet goto_set;
  56     private final ConstantPool constant_pool;
  57     private final ConstantHTML constant_html;
  58     private static boolean wide = false;
  59 
  60 
  61     CodeHTML(final String dir, final String class_name, final Method[] methods, final ConstantPool constant_pool,
  62             final ConstantHTML constant_html) throws IOException {

  63         this.class_name = class_name;
  64 //        this.methods = methods;
  65         this.constant_pool = constant_pool;
  66         this.constant_html = constant_html;

  67         file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
  68         file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
  69         for (int i = 0; i < methods.length; i++) {

  70             writeMethod(methods[i], i);
  71         }
  72         file.println("</BODY></HTML>");
  73         file.close();
  74     }
  75 
  76 
  77     /**
  78      * Disassemble a stream of byte codes and return the
  79      * string representation.
  80      *
  81      * @param  stream data input stream
  82      * @return String representation of byte code
  83      */
  84     private String codeToHTML( final ByteSequence bytes, final int method_number ) throws IOException {
  85         final short opcode = (short) bytes.readUnsignedByte();
  86         String name;
  87         String signature;
  88         int default_offset = 0;
  89         int low;
  90         int high;
  91         int index;
  92         int class_index;
  93         int vindex;
  94         int constant;
  95         int[] jump_table;
  96         int no_pad_bytes = 0;
  97         int offset;
  98         final StringBuilder buf = new StringBuilder(256); // CHECKSTYLE IGNORE MagicNumber
  99         buf.append("<TT>").append(Const.getOpcodeName(opcode)).append("</TT></TD><TD>");
 100         /* Special case: Skip (0-3) padding bytes, i.e., the
 101          * following bytes are 4-byte-aligned
 102          */
 103         if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) {
 104             final int remainder = bytes.getIndex() % 4;
 105             no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
 106             for (int i = 0; i < no_pad_bytes; i++) {

 107                 bytes.readByte();
 108             }
 109             // Both cases have a field default_offset in common
 110             default_offset = bytes.readInt();
 111         }
 112         switch (opcode) {
 113             case Const.TABLESWITCH:

 114                 low = bytes.readInt();
 115                 high = bytes.readInt();

 116                 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 117                 default_offset += offset;

 118                 buf.append("<TABLE BORDER=1><TR>");

 119                 // Print switch indices in first row (and default)
 120                 jump_table = new int[high - low + 1];
 121                 for (int i = 0; i < jump_table.length; i++) {
 122                     jump_table[i] = offset + bytes.readInt();
 123                     buf.append("<TH>").append(low + i).append("</TH>");

 124                 }
 125                 buf.append("<TH>default</TH></TR>\n<TR>");

 126                 // Print target and default indices in second row
 127             for (final int element : jump_table) {
 128                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 129                         element).append("\">").append(element).append("</A></TD>");
 130             }
 131                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 132                         default_offset).append("\">").append(default_offset).append(
 133                         "</A></TD></TR>\n</TABLE>\n");
 134                 break;

 135             /* Lookup switch has variable length arguments.
 136              */
 137             case Const.LOOKUPSWITCH:
 138                 final int npairs = bytes.readInt();
 139                 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 140                 jump_table = new int[npairs];
 141                 default_offset += offset;

 142                 buf.append("<TABLE BORDER=1><TR>");

 143                 // Print switch indices in first row (and default)
 144                 for (int i = 0; i < npairs; i++) {
 145                     final int match = bytes.readInt();

 146                     jump_table[i] = offset + bytes.readInt();
 147                     buf.append("<TH>").append(match).append("</TH>");
 148                 }
 149                 buf.append("<TH>default</TH></TR>\n<TR>");

 150                 // Print target and default indices in second row
 151                 for (int i = 0; i < npairs; i++) {
 152                     buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 153                             jump_table[i]).append("\">").append(jump_table[i]).append("</A></TD>");
 154                 }
 155                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 156                         default_offset).append("\">").append(default_offset).append(
 157                         "</A></TD></TR>\n</TABLE>\n");
 158                 break;

 159             /* Two address bytes + offset from start of byte stream form the
 160              * jump target.
 161              */
 162             case Const.GOTO:
 163             case Const.IFEQ:
 164             case Const.IFGE:
 165             case Const.IFGT:
 166             case Const.IFLE:
 167             case Const.IFLT:
 168             case Const.IFNE:
 169             case Const.IFNONNULL:
 170             case Const.IFNULL:
 171             case Const.IF_ACMPEQ:
 172             case Const.IF_ACMPNE:
 173             case Const.IF_ICMPEQ:
 174             case Const.IF_ICMPGE:
 175             case Const.IF_ICMPGT:
 176             case Const.IF_ICMPLE:
 177             case Const.IF_ICMPLT:
 178             case Const.IF_ICMPNE:
 179             case Const.JSR:
 180                 index = bytes.getIndex() + bytes.readShort() - 1;
 181                 buf.append("<A HREF=\"#code").append(method_number).append("@").append(index)
 182                         .append("\">").append(index).append("</A>");
 183                 break;

 184             /* Same for 32-bit wide jumps
 185              */
 186             case Const.GOTO_W:
 187             case Const.JSR_W:
 188                 final int windex = bytes.getIndex() + bytes.readInt() - 1;
 189                 buf.append("<A HREF=\"#code").append(method_number).append("@").append(windex)
 190                         .append("\">").append(windex).append("</A>");
 191                 break;

 192             /* Index byte references local variable (register)
 193              */
 194             case Const.ALOAD:
 195             case Const.ASTORE:
 196             case Const.DLOAD:
 197             case Const.DSTORE:
 198             case Const.FLOAD:
 199             case Const.FSTORE:
 200             case Const.ILOAD:
 201             case Const.ISTORE:
 202             case Const.LLOAD:
 203             case Const.LSTORE:
 204             case Const.RET:
 205                 if (wide) {
 206                     vindex = bytes.readShort();
 207                     wide = false; // Clear flag
 208                 } else {

 209                     vindex = bytes.readUnsignedByte();
 210                 }
 211                 buf.append("%").append(vindex);
 212                 break;

 213             /*
 214              * Remember wide byte which is used to form a 16-bit address in the
 215              * following instruction. Relies on that the method is called again with
 216              * the following opcode.
 217              */
 218             case Const.WIDE:
 219                 wide = true;
 220                 buf.append("(wide)");
 221                 break;

 222             /* Array of basic type.
 223              */
 224             case Const.NEWARRAY:
 225                 buf.append("<FONT COLOR=\"#00FF00\">").append(Const.getTypeName(bytes.readByte())).append(
 226                         "</FONT>");
 227                 break;

 228             /* Access object/class fields.
 229              */
 230             case Const.GETFIELD:
 231             case Const.GETSTATIC:
 232             case Const.PUTFIELD:
 233             case Const.PUTSTATIC:
 234                 index = bytes.readShort();
 235                 final ConstantFieldref c1 = (ConstantFieldref) constant_pool.getConstant(index,
 236                         Const.CONSTANT_Fieldref);
 237                 class_index = c1.getClassIndex();
 238                 name = constant_pool.getConstantString(class_index, Const.CONSTANT_Class);
 239                 name = Utility.compactClassName(name, false);

 240                 index = c1.getNameAndTypeIndex();
 241                 final String field_name = constant_pool.constantToString(index, Const.CONSTANT_NameAndType);
 242                 if (name.equals(class_name)) { // Local field
 243                     buf.append("<A HREF=\"").append(class_name).append("_methods.html#field")
 244                             .append(field_name).append("\" TARGET=Methods>").append(field_name)
 245                             .append("</A>\n");
 246                 } else {
 247                     buf.append(constant_html.referenceConstant(class_index)).append(".").append(
 248                             field_name);
 249                 }



 250                 break;

 251             /* Operands are references to classes in constant pool
 252              */
 253             case Const.CHECKCAST:
 254             case Const.INSTANCEOF:
 255             case Const.NEW:
 256                 index = bytes.readShort();
 257                 buf.append(constant_html.referenceConstant(index));
 258                 break;

 259             /* Operands are references to methods in constant pool
 260              */
 261             case Const.INVOKESPECIAL:
 262             case Const.INVOKESTATIC:
 263             case Const.INVOKEVIRTUAL:
 264             case Const.INVOKEINTERFACE:
 265             case Const.INVOKEDYNAMIC:
 266                 final int m_index = bytes.readShort();
 267                 String str;
 268                 if (opcode == Const.INVOKEINTERFACE) { // Special treatment needed
 269                     bytes.readUnsignedByte(); // Redundant
 270                     bytes.readUnsignedByte(); // Reserved
 271 //                    int nargs = bytes.readUnsignedByte(); // Redundant
 272 //                    int reserved = bytes.readUnsignedByte(); // Reserved
 273                     final ConstantInterfaceMethodref c = (ConstantInterfaceMethodref) constant_pool
 274                             .getConstant(m_index, Const.CONSTANT_InterfaceMethodref);
 275                     class_index = c.getClassIndex();

 276                     index = c.getNameAndTypeIndex();
 277                     name = Class2HTML.referenceClass(class_index);
 278                 } else if (opcode == Const.INVOKEDYNAMIC) { // Special treatment needed
 279                     bytes.readUnsignedByte(); // Reserved
 280                     bytes.readUnsignedByte(); // Reserved
 281                     final ConstantInvokeDynamic c = (ConstantInvokeDynamic) constant_pool
 282                             .getConstant(m_index, Const.CONSTANT_InvokeDynamic);
 283                     index = c.getNameAndTypeIndex();
 284                     name = "#" + c.getBootstrapMethodAttrIndex();
 285                 } else {
 286                     // UNDONE: Java8 now allows INVOKESPECIAL and INVOKESTATIC to
 287                     // reference EITHER a Methodref OR an InterfaceMethodref.
 288                     // Not sure if that affects this code or not.  (markro)
 289                     final ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(m_index,
 290                             Const.CONSTANT_Methodref);
 291                     class_index = c.getClassIndex();


 292                     index = c.getNameAndTypeIndex();


 293                 name = Class2HTML.referenceClass(class_index);
 294                 }
 295                 str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(
 296                         index, Const.CONSTANT_NameAndType)));
 297                 // Get signature, i.e., types
 298                 final ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(index,
 299                         Const.CONSTANT_NameAndType);
 300                 signature = constant_pool.constantToString(c2.getSignatureIndex(), Const.CONSTANT_Utf8);
 301                 final String[] args = Utility.methodSignatureArgumentTypes(signature, false);
 302                 final String type = Utility.methodSignatureReturnType(signature, false);
 303                 buf.append(name).append(".<A HREF=\"").append(class_name).append("_cp.html#cp")
 304                         .append(m_index).append("\" TARGET=ConstantPool>").append(str).append(
 305                                 "</A>").append("(");


 306                 // List arguments
 307                 for (int i = 0; i < args.length; i++) {
 308                     buf.append(Class2HTML.referenceType(args[i]));
 309                     if (i < args.length - 1) {

 310                         buf.append(", ");
 311                     }
 312                 }
 313                 // Attach return type
 314                 buf.append("):").append(Class2HTML.referenceType(type));

 315                 break;

 316             /* Operands are references to items in constant pool
 317              */
 318             case Const.LDC_W:
 319             case Const.LDC2_W:
 320                 index = bytes.readShort();
 321                 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
 322                         .append("\" TARGET=\"ConstantPool\">").append(

 323                                 Class2HTML.toHTML(constant_pool.constantToString(index,
 324                                         constant_pool.getConstant(index).getTag()))).append("</a>");


 325                 break;
 326             case Const.LDC:

 327                 index = bytes.readUnsignedByte();
 328                 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
 329                         .append("\" TARGET=\"ConstantPool\">").append(
 330                                 Class2HTML.toHTML(constant_pool.constantToString(index,
 331                                         constant_pool.getConstant(index).getTag()))).append("</a>");


 332                 break;

 333             /* Array of references.
 334              */
 335             case Const.ANEWARRAY:
 336                 index = bytes.readShort();

 337                 buf.append(constant_html.referenceConstant(index));
 338                 break;

 339             /* Multidimensional array of references.
 340              */
 341             case Const.MULTIANEWARRAY:
 342                 index = bytes.readShort();
 343                 final int dimensions = bytes.readByte();
 344                 buf.append(constant_html.referenceConstant(index)).append(":").append(dimensions)
 345                         .append("-dimensional");
 346                 break;

 347             /* Increment local variable.
 348              */
 349             case Const.IINC:
 350                 if (wide) {
 351                     vindex = bytes.readShort();
 352                     constant = bytes.readShort();
 353                     wide = false;
 354                 } else {

 355                     vindex = bytes.readUnsignedByte();
 356                     constant = bytes.readByte();
 357                 }
 358                 buf.append("%").append(vindex).append(" ").append(constant);
 359                 break;

 360             default:
 361                 if (Const.getNoOfOperands(opcode) > 0) {
 362                     for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
 363                         switch (Const.getOperandType(opcode, i)) {
 364                             case Const.T_BYTE:
 365                                 buf.append(bytes.readUnsignedByte());
 366                                 break;
 367                             case Const.T_SHORT: // Either branch or index

 368                                 buf.append(bytes.readShort());
 369                                 break;
 370                             case Const.T_INT:

 371                                 buf.append(bytes.readInt());
 372                                 break;

 373                             default: // Never reached
 374                                 throw new IllegalStateException(
 375                                         "Unreachable default case reached! " +
 376                                                 Const.getOperandType(opcode, i));
 377                         }
 378                         buf.append("&nbsp;");
 379                     }
 380                 }
 381         }

 382         buf.append("</TD>");
 383         return buf.toString();
 384     }
 385 
 386 
 387     /**
 388      * Find all target addresses in code, so that they can be marked
 389      * with &lt;A NAME = ...&gt;. Target addresses are kept in an BitSet object.
 390      */
 391     private void findGotos( final ByteSequence bytes, final Code code ) throws IOException {


 392         int index;
 393         goto_set = new BitSet(bytes.available());
 394         int opcode;

 395         /* First get Code attribute from method and the exceptions handled
 396          * (try .. catch) in this method. We only need the line number here.
 397          */
 398         if (code != null) {
 399             final CodeException[] ce = code.getExceptionTable();
 400             for (final CodeException cex : ce) {
 401                 goto_set.set(cex.getStartPC());
 402                 goto_set.set(cex.getEndPC());
 403                 goto_set.set(cex.getHandlerPC());



 404             }

 405             // Look for local variables and their range
 406             final Attribute[] attributes = code.getAttributes();
 407             for (final Attribute attribute : attributes) {
 408                 if (attribute.getTag() == Const.ATTR_LOCAL_VARIABLE_TABLE) {
 409                     final LocalVariable[] vars = ((LocalVariableTable) attribute)
 410                             .getLocalVariableTable();
 411                     for (final LocalVariable var : vars) {
 412                         final int start = var.getStartPC();
 413                         final int end = start + var.getLength();
 414                         goto_set.set(start);
 415                         goto_set.set(end);
 416                     }
 417                     break;
 418                 }
 419             }
 420         }

 421         // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
 422         for (; bytes.available() > 0;) {
 423             opcode = bytes.readUnsignedByte();
 424             //System.out.println(getOpcodeName(opcode));
 425             switch (opcode) {
 426                 case Const.TABLESWITCH:
 427                 case Const.LOOKUPSWITCH:
 428                     //bytes.readByte(); // Skip already read byte
 429                     final int remainder = bytes.getIndex() % 4;
 430                     final int no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
 431                     int default_offset;
 432                     int offset;
 433                     for (int j = 0; j < no_pad_bytes; j++) {

 434                         bytes.readByte();
 435                     }
 436                     // Both cases have a field default_offset in common
 437                     default_offset = bytes.readInt();
 438                     if (opcode == Const.TABLESWITCH) {
 439                         final int low = bytes.readInt();
 440                         final int high = bytes.readInt();


 441                         offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 442                         default_offset += offset;
 443                         goto_set.set(default_offset);
 444                         for (int j = 0; j < (high - low + 1); j++) {

 445                             index = offset + bytes.readInt();
 446                             goto_set.set(index);
 447                         }
 448                     } else { // LOOKUPSWITCH
 449                         final int npairs = bytes.readInt();


 450                         offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 451                         default_offset += offset;
 452                         goto_set.set(default_offset);
 453                         for (int j = 0; j < npairs; j++) {
 454 //                            int match = bytes.readInt();
 455                             bytes.readInt();

 456                             index = offset + bytes.readInt();
 457                             goto_set.set(index);
 458                         }
 459                     }
 460                     break;
 461                 case Const.GOTO:
 462                 case Const.IFEQ:
 463                 case Const.IFGE:
 464                 case Const.IFGT:
 465                 case Const.IFLE:
 466                 case Const.IFLT:
 467                 case Const.IFNE:
 468                 case Const.IFNONNULL:
 469                 case Const.IFNULL:
 470                 case Const.IF_ACMPEQ:
 471                 case Const.IF_ACMPNE:
 472                 case Const.IF_ICMPEQ:
 473                 case Const.IF_ICMPGE:
 474                 case Const.IF_ICMPGT:
 475                 case Const.IF_ICMPLE:
 476                 case Const.IF_ICMPLT:
 477                 case Const.IF_ICMPNE:
 478                 case Const.JSR:
 479                     //bytes.readByte(); // Skip already read byte
 480                     index = bytes.getIndex() + bytes.readShort() - 1;

 481                     goto_set.set(index);
 482                     break;
 483                 case Const.GOTO_W:
 484                 case Const.JSR_W:
 485                     //bytes.readByte(); // Skip already read byte
 486                     index = bytes.getIndex() + bytes.readInt() - 1;
 487                     goto_set.set(index);
 488                     break;

 489                 default:
 490                     bytes.unreadByte();
 491                     codeToHTML(bytes, 0); // Ignore output
 492             }
 493         }
 494     }
 495 
 496 
 497     /**
 498      * Write a single method with the byte code associated with it.
 499      */
 500     private void writeMethod( final Method method, final int method_number ) throws IOException {


 501         // Get raw signature
 502         final String signature = method.getSignature();
 503         // Get array of strings containing the argument types
 504         final String[] args = Utility.methodSignatureArgumentTypes(signature, false);
 505         // Get return type string
 506         final String type = Utility.methodSignatureReturnType(signature, false);
 507         // Get method name
 508         final String name = method.getName();
 509         final String html_name = Class2HTML.toHTML(name);
 510         // Get method's access flags
 511         String access = Utility.accessToString(method.getAccessFlags());
 512         access = Utility.replace(access, " ", "&nbsp;");
 513         // Get the method's attributes, the Code Attribute in particular
 514         final Attribute[] attributes = method.getAttributes();
 515         file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT>&nbsp;" + "<A NAME=method"
 516                 + method_number + ">" + Class2HTML.referenceType(type) + "</A>&nbsp<A HREF=\""
 517                 + class_name + "_methods.html#method" + method_number + "\" TARGET=Methods>"
 518                 + html_name + "</A>(");
 519         for (int i = 0; i < args.length; i++) {


 520             file.print(Class2HTML.referenceType(args[i]));
 521             if (i < args.length - 1) {
 522                 file.print(",&nbsp;");
 523             }
 524         }
 525         file.println(")</B></P>");
 526         Code c = null;
 527         byte[] code = null;
 528         if (attributes.length > 0) {


 529             file.print("<H4>Attributes</H4><UL>\n");
 530             for (int i = 0; i < attributes.length; i++) {
 531                 byte tag = attributes[i].getTag();
 532                 if (tag != Const.ATTR_UNKNOWN) {
 533                     file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method"
 534                             + method_number + "@" + i + "\" TARGET=Attributes>"
 535                             + Const.getAttributeName(tag) + "</A></LI>\n");
 536                 } else {
 537                     file.print("<LI>" + attributes[i] + "</LI>");
 538                 }
 539                 if (tag == Const.ATTR_CODE) {
 540                     c = (Code) attributes[i];
 541                     final Attribute[] attributes2 = c.getAttributes();
 542                     code = c.getCode();

 543                     file.print("<UL>");
 544                     for (int j = 0; j < attributes2.length; j++) {
 545                         tag = attributes2[j].getTag();
 546                         file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" + "method"
 547                                 + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>"
 548                                 + Const.getAttributeName(tag) + "</A></LI>\n");

 549                     }
 550                     file.print("</UL>");
 551                 }
 552             }
 553             file.println("</UL>");
 554         }
 555         if (code != null) { // No code, an abstract method, e.g.

 556             //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));

 557             // Print the byte code
 558             try (ByteSequence stream = new ByteSequence(code)) {
 559                 stream.mark(stream.available());
 560                 findGotos(stream, c);
 561                 stream.reset();
 562                 file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>"
 563                         + "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
 564                 for (; stream.available() > 0;) {
 565                     final int offset = stream.getIndex();
 566                     final String str = codeToHTML(stream, method_number);


 567                     String anchor = "";
 568                     /*
 569                      * Set an anchor mark if this line is targetted by a goto, jsr, etc. Defining an anchor for every
 570                      * line is very inefficient!
 571                      */
 572                     if (goto_set.get(offset)) {
 573                         anchor = "<A NAME=code" + method_number + "@" + offset + "></A>";
 574                     }
 575                     String anchor2;
 576                     if (stream.getIndex() == code.length) {
 577                         anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
 578                     } else {
 579                         anchor2 = "" + offset;
 580                     }
 581                     file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
 582                 }
 583             }
 584             // Mark last line, may be targetted from Attributes window
 585             file.println("<TR><TD> </A></TD></TR>");
 586             file.println("</TABLE>");
 587         }

 588     }
 589 }
< prev index next >