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

This page was automatically generated by Maven