1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   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  *
  48  */
  49 final class CodeHTML {
  50 
  51     private final String class_name; // name of current class
  52 //    private Method[] methods; // Methods to print
  53     private final PrintWriter file; // file to write to
  54     private BitSet goto_set;
  55     private final ConstantPool constant_pool;
  56     private final ConstantHTML constant_html;
  57     private static boolean wide = false;
  58 
  59 
  60     CodeHTML(final String dir, final String class_name, final Method[] methods, final ConstantPool constant_pool,
  61             final ConstantHTML constant_html) throws IOException {
  62         this.class_name = class_name;
  63 //        this.methods = methods;
  64         this.constant_pool = constant_pool;
  65         this.constant_html = constant_html;
  66         file = new PrintWriter(new FileOutputStream(dir + class_name + "_code.html"));
  67         file.println("<HTML><BODY BGCOLOR=\"#C0C0C0\">");
  68         for (int i = 0; i < methods.length; i++) {
  69             writeMethod(methods[i], i);
  70         }
  71         file.println("</BODY></HTML>");
  72         file.close();
  73     }
  74 
  75 
  76     /**
  77      * Disassemble a stream of byte codes and return the
  78      * string representation.
  79      *
  80      * @param  stream data input stream
  81      * @return String representation of byte code
  82      */
  83     private String codeToHTML( final ByteSequence bytes, final int method_number ) throws IOException {
  84         final short opcode = (short) bytes.readUnsignedByte();
  85         String name;
  86         String signature;
  87         int default_offset = 0;
  88         int low;
  89         int high;
  90         int index;
  91         int class_index;
  92         int vindex;
  93         int constant;
  94         int[] jump_table;
  95         int no_pad_bytes = 0;
  96         int offset;
  97         final StringBuilder buf = new StringBuilder(256); // CHECKSTYLE IGNORE MagicNumber
  98         buf.append("<TT>").append(Const.getOpcodeName(opcode)).append("</TT></TD><TD>");
  99         /* Special case: Skip (0-3) padding bytes, i.e., the
 100          * following bytes are 4-byte-aligned
 101          */
 102         if ((opcode == Const.TABLESWITCH) || (opcode == Const.LOOKUPSWITCH)) {
 103             final int remainder = bytes.getIndex() % 4;
 104             no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
 105             for (int i = 0; i < no_pad_bytes; i++) {
 106                 bytes.readByte();
 107             }
 108             // Both cases have a field default_offset in common
 109             default_offset = bytes.readInt();
 110         }
 111         switch (opcode) {
 112             case Const.TABLESWITCH:
 113                 low = bytes.readInt();
 114                 high = bytes.readInt();
 115                 offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 116                 default_offset += offset;
 117                 buf.append("<TABLE BORDER=1><TR>");
 118                 // Print switch indices in first row (and default)
 119                 jump_table = new int[high - low + 1];
 120                 for (int i = 0; i < jump_table.length; i++) {
 121                     jump_table[i] = offset + bytes.readInt();
 122                     buf.append("<TH>").append(low + i).append("</TH>");
 123                 }
 124                 buf.append("<TH>default</TH></TR>\n<TR>");
 125                 // Print target and default indices in second row
 126             for (final int element : jump_table) {
 127                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 128                         element).append("\">").append(element).append("</A></TD>");
 129             }
 130                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 131                         default_offset).append("\">").append(default_offset).append(
 132                         "</A></TD></TR>\n</TABLE>\n");
 133                 break;
 134             /* Lookup switch has variable length arguments.
 135              */
 136             case Const.LOOKUPSWITCH:
 137                 final int npairs = bytes.readInt();
 138                 offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 139                 jump_table = new int[npairs];
 140                 default_offset += offset;
 141                 buf.append("<TABLE BORDER=1><TR>");
 142                 // Print switch indices in first row (and default)
 143                 for (int i = 0; i < npairs; i++) {
 144                     final int match = bytes.readInt();
 145                     jump_table[i] = offset + bytes.readInt();
 146                     buf.append("<TH>").append(match).append("</TH>");
 147                 }
 148                 buf.append("<TH>default</TH></TR>\n<TR>");
 149                 // Print target and default indices in second row
 150                 for (int i = 0; i < npairs; i++) {
 151                     buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 152                             jump_table[i]).append("\">").append(jump_table[i]).append("</A></TD>");
 153                 }
 154                 buf.append("<TD><A HREF=\"#code").append(method_number).append("@").append(
 155                         default_offset).append("\">").append(default_offset).append(
 156                         "</A></TD></TR>\n</TABLE>\n");
 157                 break;
 158             /* Two address bytes + offset from start of byte stream form the
 159              * jump target.
 160              */
 161             case Const.GOTO:
 162             case Const.IFEQ:
 163             case Const.IFGE:
 164             case Const.IFGT:
 165             case Const.IFLE:
 166             case Const.IFLT:
 167             case Const.IFNE:
 168             case Const.IFNONNULL:
 169             case Const.IFNULL:
 170             case Const.IF_ACMPEQ:
 171             case Const.IF_ACMPNE:
 172             case Const.IF_ICMPEQ:
 173             case Const.IF_ICMPGE:
 174             case Const.IF_ICMPGT:
 175             case Const.IF_ICMPLE:
 176             case Const.IF_ICMPLT:
 177             case Const.IF_ICMPNE:
 178             case Const.JSR:
 179                 index = bytes.getIndex() + bytes.readShort() - 1;
 180                 buf.append("<A HREF=\"#code").append(method_number).append("@").append(index)
 181                         .append("\">").append(index).append("</A>");
 182                 break;
 183             /* Same for 32-bit wide jumps
 184              */
 185             case Const.GOTO_W:
 186             case Const.JSR_W:
 187                 final int windex = bytes.getIndex() + bytes.readInt() - 1;
 188                 buf.append("<A HREF=\"#code").append(method_number).append("@").append(windex)
 189                         .append("\">").append(windex).append("</A>");
 190                 break;
 191             /* Index byte references local variable (register)
 192              */
 193             case Const.ALOAD:
 194             case Const.ASTORE:
 195             case Const.DLOAD:
 196             case Const.DSTORE:
 197             case Const.FLOAD:
 198             case Const.FSTORE:
 199             case Const.ILOAD:
 200             case Const.ISTORE:
 201             case Const.LLOAD:
 202             case Const.LSTORE:
 203             case Const.RET:
 204                 if (wide) {
 205                     vindex = bytes.readShort();
 206                     wide = false; // Clear flag
 207                 } else {
 208                     vindex = bytes.readUnsignedByte();
 209                 }
 210                 buf.append("%").append(vindex);
 211                 break;
 212             /*
 213              * Remember wide byte which is used to form a 16-bit address in the
 214              * following instruction. Relies on that the method is called again with
 215              * the following opcode.
 216              */
 217             case Const.WIDE:
 218                 wide = true;
 219                 buf.append("(wide)");
 220                 break;
 221             /* Array of basic type.
 222              */
 223             case Const.NEWARRAY:
 224                 buf.append("<FONT COLOR=\"#00FF00\">").append(Const.getTypeName(bytes.readByte())).append(
 225                         "</FONT>");
 226                 break;
 227             /* Access object/class fields.
 228              */
 229             case Const.GETFIELD:
 230             case Const.GETSTATIC:
 231             case Const.PUTFIELD:
 232             case Const.PUTSTATIC:
 233                 index = bytes.readShort();
 234                 final ConstantFieldref c1 = (ConstantFieldref) constant_pool.getConstant(index,
 235                         Const.CONSTANT_Fieldref);
 236                 class_index = c1.getClassIndex();
 237                 name = constant_pool.getConstantString(class_index, Const.CONSTANT_Class);
 238                 name = Utility.compactClassName(name, false);
 239                 index = c1.getNameAndTypeIndex();
 240                 final String field_name = constant_pool.constantToString(index, Const.CONSTANT_NameAndType);
 241                 if (name.equals(class_name)) { // Local field
 242                     buf.append("<A HREF=\"").append(class_name).append("_methods.html#field")
 243                             .append(field_name).append("\" TARGET=Methods>").append(field_name)
 244                             .append("</A>\n");
 245                 } else {
 246                     buf.append(constant_html.referenceConstant(class_index)).append(".").append(
 247                             field_name);
 248                 }
 249                 break;
 250             /* Operands are references to classes in constant pool
 251              */
 252             case Const.CHECKCAST:
 253             case Const.INSTANCEOF:
 254             case Const.NEW:
 255                 index = bytes.readShort();
 256                 buf.append(constant_html.referenceConstant(index));
 257                 break;
 258             /* Operands are references to methods in constant pool
 259              */
 260             case Const.INVOKESPECIAL:
 261             case Const.INVOKESTATIC:
 262             case Const.INVOKEVIRTUAL:
 263             case Const.INVOKEINTERFACE:
 264             case Const.INVOKEDYNAMIC:
 265                 final int m_index = bytes.readShort();
 266                 String str;
 267                 if (opcode == Const.INVOKEINTERFACE) { // Special treatment needed
 268                     bytes.readUnsignedByte(); // Redundant
 269                     bytes.readUnsignedByte(); // Reserved
 270 //                    int nargs = bytes.readUnsignedByte(); // Redundant
 271 //                    int reserved = bytes.readUnsignedByte(); // Reserved
 272                     final ConstantInterfaceMethodref c = (ConstantInterfaceMethodref) constant_pool
 273                             .getConstant(m_index, Const.CONSTANT_InterfaceMethodref);
 274                     class_index = c.getClassIndex();
 275                     index = c.getNameAndTypeIndex();
 276                     name = Class2HTML.referenceClass(class_index);
 277                 } else if (opcode == Const.INVOKEDYNAMIC) { // Special treatment needed
 278                     bytes.readUnsignedByte(); // Reserved
 279                     bytes.readUnsignedByte(); // Reserved
 280                     final ConstantInvokeDynamic c = (ConstantInvokeDynamic) constant_pool
 281                             .getConstant(m_index, Const.CONSTANT_InvokeDynamic);
 282                     index = c.getNameAndTypeIndex();
 283                     name = "#" + c.getBootstrapMethodAttrIndex();
 284                 } else {
 285                     // UNDONE: Java8 now allows INVOKESPECIAL and INVOKESTATIC to
 286                     // reference EITHER a Methodref OR an InterfaceMethodref.
 287                     // Not sure if that affects this code or not.  (markro)
 288                     final ConstantMethodref c = (ConstantMethodref) constant_pool.getConstant(m_index,
 289                             Const.CONSTANT_Methodref);
 290                     class_index = c.getClassIndex();
 291                     index = c.getNameAndTypeIndex();
 292                 name = Class2HTML.referenceClass(class_index);
 293                 }
 294                 str = Class2HTML.toHTML(constant_pool.constantToString(constant_pool.getConstant(
 295                         index, Const.CONSTANT_NameAndType)));
 296                 // Get signature, i.e., types
 297                 final ConstantNameAndType c2 = (ConstantNameAndType) constant_pool.getConstant(index,
 298                         Const.CONSTANT_NameAndType);
 299                 signature = constant_pool.constantToString(c2.getSignatureIndex(), Const.CONSTANT_Utf8);
 300                 final String[] args = Utility.methodSignatureArgumentTypes(signature, false);
 301                 final String type = Utility.methodSignatureReturnType(signature, false);
 302                 buf.append(name).append(".<A HREF=\"").append(class_name).append("_cp.html#cp")
 303                         .append(m_index).append("\" TARGET=ConstantPool>").append(str).append(
 304                                 "</A>").append("(");
 305                 // List arguments
 306                 for (int i = 0; i < args.length; i++) {
 307                     buf.append(Class2HTML.referenceType(args[i]));
 308                     if (i < args.length - 1) {
 309                         buf.append(", ");
 310                     }
 311                 }
 312                 // Attach return type
 313                 buf.append("):").append(Class2HTML.referenceType(type));
 314                 break;
 315             /* Operands are references to items in constant pool
 316              */
 317             case Const.LDC_W:
 318             case Const.LDC2_W:
 319                 index = bytes.readShort();
 320                 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
 321                         .append("\" TARGET=\"ConstantPool\">").append(
 322                                 Class2HTML.toHTML(constant_pool.constantToString(index,
 323                                         constant_pool.getConstant(index).getTag()))).append("</a>");
 324                 break;
 325             case Const.LDC:
 326                 index = bytes.readUnsignedByte();
 327                 buf.append("<A HREF=\"").append(class_name).append("_cp.html#cp").append(index)
 328                         .append("\" TARGET=\"ConstantPool\">").append(
 329                                 Class2HTML.toHTML(constant_pool.constantToString(index,
 330                                         constant_pool.getConstant(index).getTag()))).append("</a>");
 331                 break;
 332             /* Array of references.
 333              */
 334             case Const.ANEWARRAY:
 335                 index = bytes.readShort();
 336                 buf.append(constant_html.referenceConstant(index));
 337                 break;
 338             /* Multidimensional array of references.
 339              */
 340             case Const.MULTIANEWARRAY:
 341                 index = bytes.readShort();
 342                 final int dimensions = bytes.readByte();
 343                 buf.append(constant_html.referenceConstant(index)).append(":").append(dimensions)
 344                         .append("-dimensional");
 345                 break;
 346             /* Increment local variable.
 347              */
 348             case Const.IINC:
 349                 if (wide) {
 350                     vindex = bytes.readShort();
 351                     constant = bytes.readShort();
 352                     wide = false;
 353                 } else {
 354                     vindex = bytes.readUnsignedByte();
 355                     constant = bytes.readByte();
 356                 }
 357                 buf.append("%").append(vindex).append(" ").append(constant);
 358                 break;
 359             default:
 360                 if (Const.getNoOfOperands(opcode) > 0) {
 361                     for (int i = 0; i < Const.getOperandTypeCount(opcode); i++) {
 362                         switch (Const.getOperandType(opcode, i)) {
 363                             case Const.T_BYTE:
 364                                 buf.append(bytes.readUnsignedByte());
 365                                 break;
 366                             case Const.T_SHORT: // Either branch or index
 367                                 buf.append(bytes.readShort());
 368                                 break;
 369                             case Const.T_INT:
 370                                 buf.append(bytes.readInt());
 371                                 break;
 372                             default: // Never reached
 373                                 throw new IllegalStateException(
 374                                         "Unreachable default case reached! " +
 375                                                 Const.getOperandType(opcode, i));
 376                         }
 377                         buf.append("&nbsp;");
 378                     }
 379                 }
 380         }
 381         buf.append("</TD>");
 382         return buf.toString();
 383     }
 384 
 385 
 386     /**
 387      * Find all target addresses in code, so that they can be marked
 388      * with &lt;A NAME = ...&gt;. Target addresses are kept in an BitSet object.
 389      */
 390     private void findGotos( final ByteSequence bytes, final Code code ) throws IOException {
 391         int index;
 392         goto_set = new BitSet(bytes.available());
 393         int opcode;
 394         /* First get Code attribute from method and the exceptions handled
 395          * (try .. catch) in this method. We only need the line number here.
 396          */
 397         if (code != null) {
 398             final CodeException[] ce = code.getExceptionTable();
 399             for (final CodeException cex : ce) {
 400                 goto_set.set(cex.getStartPC());
 401                 goto_set.set(cex.getEndPC());
 402                 goto_set.set(cex.getHandlerPC());
 403             }
 404             // Look for local variables and their range
 405             final Attribute[] attributes = code.getAttributes();
 406             for (final Attribute attribute : attributes) {
 407                 if (attribute.getTag() == Const.ATTR_LOCAL_VARIABLE_TABLE) {
 408                     final LocalVariable[] vars = ((LocalVariableTable) attribute)
 409                             .getLocalVariableTable();
 410                     for (final LocalVariable var : vars) {
 411                         final int start = var.getStartPC();
 412                         final int end = start + var.getLength();
 413                         goto_set.set(start);
 414                         goto_set.set(end);
 415                     }
 416                     break;
 417                 }
 418             }
 419         }
 420         // Get target addresses from GOTO, JSR, TABLESWITCH, etc.
 421         for (; bytes.available() > 0;) {
 422             opcode = bytes.readUnsignedByte();
 423             //System.out.println(getOpcodeName(opcode));
 424             switch (opcode) {
 425                 case Const.TABLESWITCH:
 426                 case Const.LOOKUPSWITCH:
 427                     //bytes.readByte(); // Skip already read byte
 428                     final int remainder = bytes.getIndex() % 4;
 429                     final int no_pad_bytes = (remainder == 0) ? 0 : 4 - remainder;
 430                     int default_offset;
 431                     int offset;
 432                     for (int j = 0; j < no_pad_bytes; j++) {
 433                         bytes.readByte();
 434                     }
 435                     // Both cases have a field default_offset in common
 436                     default_offset = bytes.readInt();
 437                     if (opcode == Const.TABLESWITCH) {
 438                         final int low = bytes.readInt();
 439                         final int high = bytes.readInt();
 440                         offset = bytes.getIndex() - 12 - no_pad_bytes - 1;
 441                         default_offset += offset;
 442                         goto_set.set(default_offset);
 443                         for (int j = 0; j < (high - low + 1); j++) {
 444                             index = offset + bytes.readInt();
 445                             goto_set.set(index);
 446                         }
 447                     } else { // LOOKUPSWITCH
 448                         final int npairs = bytes.readInt();
 449                         offset = bytes.getIndex() - 8 - no_pad_bytes - 1;
 450                         default_offset += offset;
 451                         goto_set.set(default_offset);
 452                         for (int j = 0; j < npairs; j++) {
 453 //                            int match = bytes.readInt();
 454                             bytes.readInt();
 455                             index = offset + bytes.readInt();
 456                             goto_set.set(index);
 457                         }
 458                     }
 459                     break;
 460                 case Const.GOTO:
 461                 case Const.IFEQ:
 462                 case Const.IFGE:
 463                 case Const.IFGT:
 464                 case Const.IFLE:
 465                 case Const.IFLT:
 466                 case Const.IFNE:
 467                 case Const.IFNONNULL:
 468                 case Const.IFNULL:
 469                 case Const.IF_ACMPEQ:
 470                 case Const.IF_ACMPNE:
 471                 case Const.IF_ICMPEQ:
 472                 case Const.IF_ICMPGE:
 473                 case Const.IF_ICMPGT:
 474                 case Const.IF_ICMPLE:
 475                 case Const.IF_ICMPLT:
 476                 case Const.IF_ICMPNE:
 477                 case Const.JSR:
 478                     //bytes.readByte(); // Skip already read byte
 479                     index = bytes.getIndex() + bytes.readShort() - 1;
 480                     goto_set.set(index);
 481                     break;
 482                 case Const.GOTO_W:
 483                 case Const.JSR_W:
 484                     //bytes.readByte(); // Skip already read byte
 485                     index = bytes.getIndex() + bytes.readInt() - 1;
 486                     goto_set.set(index);
 487                     break;
 488                 default:
 489                     bytes.unreadByte();
 490                     codeToHTML(bytes, 0); // Ignore output
 491             }
 492         }
 493     }
 494 
 495 
 496     /**
 497      * Write a single method with the byte code associated with it.
 498      */
 499     private void writeMethod( final Method method, final int method_number ) throws IOException {
 500         // Get raw signature
 501         final String signature = method.getSignature();
 502         // Get array of strings containing the argument types
 503         final String[] args = Utility.methodSignatureArgumentTypes(signature, false);
 504         // Get return type string
 505         final String type = Utility.methodSignatureReturnType(signature, false);
 506         // Get method name
 507         final String name = method.getName();
 508         final String html_name = Class2HTML.toHTML(name);
 509         // Get method's access flags
 510         String access = Utility.accessToString(method.getAccessFlags());
 511         access = Utility.replace(access, " ", "&nbsp;");
 512         // Get the method's attributes, the Code Attribute in particular
 513         final Attribute[] attributes = method.getAttributes();
 514         file.print("<P><B><FONT COLOR=\"#FF0000\">" + access + "</FONT>&nbsp;" + "<A NAME=method"
 515                 + method_number + ">" + Class2HTML.referenceType(type) + "</A>&nbsp<A HREF=\""
 516                 + class_name + "_methods.html#method" + method_number + "\" TARGET=Methods>"
 517                 + html_name + "</A>(");
 518         for (int i = 0; i < args.length; i++) {
 519             file.print(Class2HTML.referenceType(args[i]));
 520             if (i < args.length - 1) {
 521                 file.print(",&nbsp;");
 522             }
 523         }
 524         file.println(")</B></P>");
 525         Code c = null;
 526         byte[] code = null;
 527         if (attributes.length > 0) {
 528             file.print("<H4>Attributes</H4><UL>\n");
 529             for (int i = 0; i < attributes.length; i++) {
 530                 byte tag = attributes[i].getTag();
 531                 if (tag != Const.ATTR_UNKNOWN) {
 532                     file.print("<LI><A HREF=\"" + class_name + "_attributes.html#method"
 533                             + method_number + "@" + i + "\" TARGET=Attributes>"
 534                             + Const.getAttributeName(tag) + "</A></LI>\n");
 535                 } else {
 536                     file.print("<LI>" + attributes[i] + "</LI>");
 537                 }
 538                 if (tag == Const.ATTR_CODE) {
 539                     c = (Code) attributes[i];
 540                     final Attribute[] attributes2 = c.getAttributes();
 541                     code = c.getCode();
 542                     file.print("<UL>");
 543                     for (int j = 0; j < attributes2.length; j++) {
 544                         tag = attributes2[j].getTag();
 545                         file.print("<LI><A HREF=\"" + class_name + "_attributes.html#" + "method"
 546                                 + method_number + "@" + i + "@" + j + "\" TARGET=Attributes>"
 547                                 + Const.getAttributeName(tag) + "</A></LI>\n");
 548                     }
 549                     file.print("</UL>");
 550                 }
 551             }
 552             file.println("</UL>");
 553         }
 554         if (code != null) { // No code, an abstract method, e.g.
 555             //System.out.println(name + "\n" + Utility.codeToString(code, constant_pool, 0, -1));
 556             // Print the byte code
 557             try (ByteSequence stream = new ByteSequence(code)) {
 558                 stream.mark(stream.available());
 559                 findGotos(stream, c);
 560                 stream.reset();
 561                 file.println("<TABLE BORDER=0><TR><TH ALIGN=LEFT>Byte<BR>offset</TH>"
 562                         + "<TH ALIGN=LEFT>Instruction</TH><TH ALIGN=LEFT>Argument</TH>");
 563                 for (; stream.available() > 0;) {
 564                     final int offset = stream.getIndex();
 565                     final String str = codeToHTML(stream, method_number);
 566                     String anchor = "";
 567                     /*
 568                      * Set an anchor mark if this line is targetted by a goto, jsr, etc. Defining an anchor for every
 569                      * line is very inefficient!
 570                      */
 571                     if (goto_set.get(offset)) {
 572                         anchor = "<A NAME=code" + method_number + "@" + offset + "></A>";
 573                     }
 574                     String anchor2;
 575                     if (stream.getIndex() == code.length) {
 576                         anchor2 = "<A NAME=code" + method_number + "@" + code.length + ">" + offset + "</A>";
 577                     } else {
 578                         anchor2 = "" + offset;
 579                     }
 580                     file.println("<TR VALIGN=TOP><TD>" + anchor2 + "</TD><TD>" + anchor + str + "</TR>");
 581                 }
 582             }
 583             // Mark last line, may be targetted from Attributes window
 584             file.println("<TR><TD> </A></TD></TR>");
 585             file.println("</TABLE>");
 586         }
 587     }
 588 }