1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 package org.apache.bcel.classfile; 19 20 import java.io.DataInput; 21 import java.io.DataOutputStream; 22 import java.io.IOException; 23 24 import org.apache.bcel.Const; 25 26 /** 27 * This class represents a table of line numbers for debugging 28 * purposes. This attribute is used by the <em>Code</em> attribute. It 29 * contains pairs of PCs and line numbers. 30 * 31 * @version $Id$ 32 * @see Code 33 * @see LineNumber 34 */ 35 public final class LineNumberTable extends Attribute { 36 37 private static final int MAX_LINE_LENGTH = 72; 38 private LineNumber[] line_number_table; // Table of line/numbers pairs 39 40 41 /* 42 * Initialize from another object. Note that both objects use the same 43 * references (shallow copy). Use copy() for a physical copy. 44 */ LineNumberTable(final LineNumberTable c)45 public LineNumberTable(final LineNumberTable c) { 46 this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool()); 47 } 48 49 50 /* 51 * @param name_index Index of name 52 * @param length Content length in bytes 53 * @param line_number_table Table of line/numbers pairs 54 * @param constant_pool Array of constants 55 */ LineNumberTable(final int name_index, final int length, final LineNumber[] line_number_table, final ConstantPool constant_pool)56 public LineNumberTable(final int name_index, final int length, final LineNumber[] line_number_table, 57 final ConstantPool constant_pool) { 58 super(Const.ATTR_LINE_NUMBER_TABLE, name_index, length, constant_pool); 59 this.line_number_table = line_number_table; 60 } 61 62 63 /** 64 * Construct object from input stream. 65 * @param name_index Index of name 66 * @param length Content length in bytes 67 * @param input Input stream 68 * @param constant_pool Array of constants 69 * @throws IOEXception if an I/O Exception occurs in readUnsignedShort 70 */ LineNumberTable(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool)71 LineNumberTable(final int name_index, final int length, final DataInput input, final ConstantPool constant_pool) 72 throws IOException { 73 this(name_index, length, (LineNumber[]) null, constant_pool); 74 final int line_number_table_length = input.readUnsignedShort(); 75 line_number_table = new LineNumber[line_number_table_length]; 76 for (int i = 0; i < line_number_table_length; i++) { 77 line_number_table[i] = new LineNumber(input); 78 } 79 } 80 81 82 /** 83 * Called by objects that are traversing the nodes of the tree implicitely 84 * defined by the contents of a Java class. I.e., the hierarchy of methods, 85 * fields, attributes, etc. spawns a tree of objects. 86 * 87 * @param v Visitor object 88 */ 89 @Override accept( final Visitor v )90 public void accept( final Visitor v ) { 91 v.visitLineNumberTable(this); 92 } 93 94 95 /** 96 * Dump line number table attribute to file stream in binary format. 97 * 98 * @param file Output file stream 99 * @throws IOEXception if an I/O Exception occurs in writeShort 100 */ 101 @Override dump( final DataOutputStream file )102 public final void dump( final DataOutputStream file ) throws IOException { 103 super.dump(file); 104 file.writeShort(line_number_table.length); 105 for (final LineNumber lineNumber : line_number_table) { 106 lineNumber.dump(file); 107 } 108 } 109 110 111 /** 112 * @return Array of (pc offset, line number) pairs. 113 */ getLineNumberTable()114 public final LineNumber[] getLineNumberTable() { 115 return line_number_table; 116 } 117 118 119 /** 120 * @param line_number_table the line number entries for this table 121 */ setLineNumberTable( final LineNumber[] line_number_table )122 public final void setLineNumberTable( final LineNumber[] line_number_table ) { 123 this.line_number_table = line_number_table; 124 } 125 126 127 /** 128 * @return String representation. 129 */ 130 @Override toString()131 public final String toString() { 132 final StringBuilder buf = new StringBuilder(); 133 final StringBuilder line = new StringBuilder(); 134 final String newLine = System.getProperty("line.separator", "\n"); 135 for (int i = 0; i < line_number_table.length; i++) { 136 line.append(line_number_table[i].toString()); 137 if (i < line_number_table.length - 1) { 138 line.append(", "); 139 } 140 if ((line.length() > MAX_LINE_LENGTH) && (i < line_number_table.length - 1)) { 141 line.append(newLine); 142 buf.append(line); 143 line.setLength(0); 144 } 145 } 146 buf.append(line); 147 return buf.toString(); 148 } 149 150 151 /** 152 * Map byte code positions to source code lines. 153 * 154 * @param pos byte code offset 155 * @return corresponding line in source code 156 */ getSourceLine( final int pos )157 public int getSourceLine( final int pos ) { 158 int l = 0; 159 int r = line_number_table.length - 1; 160 if (r < 0) { 161 return -1; 162 } 163 int min_index = -1; 164 int min = -1; 165 /* Do a binary search since the array is ordered. 166 */ 167 do { 168 final int i = (l + r) / 2; 169 final int j = line_number_table[i].getStartPC(); 170 if (j == pos) { 171 return line_number_table[i].getLineNumber(); 172 } else if (pos < j) { 173 r = i - 1; 174 } else { 175 l = i + 1; 176 } 177 /* If exact match can't be found (which is the most common case) 178 * return the line number that corresponds to the greatest index less 179 * than pos. 180 */ 181 if (j < pos && j > min) { 182 min = j; 183 min_index = i; 184 } 185 } while (l <= r); 186 /* It's possible that we did not find any valid entry for the bytecode 187 * offset we were looking for. 188 */ 189 if (min_index < 0) { 190 return -1; 191 } 192 return line_number_table[min_index].getLineNumber(); 193 } 194 195 196 /** 197 * @return deep copy of this attribute 198 */ 199 @Override copy( final ConstantPool _constant_pool )200 public Attribute copy( final ConstantPool _constant_pool ) { 201 // TODO could use the lower level constructor and thereby allow 202 // line_number_table to be made final 203 final LineNumberTable c = (LineNumberTable) clone(); 204 c.line_number_table = new LineNumber[line_number_table.length]; 205 for (int i = 0; i < line_number_table.length; i++) { 206 c.line_number_table[i] = line_number_table[i].copy(); 207 } 208 c.setConstantPool(_constant_pool); 209 return c; 210 } 211 212 getTableLength()213 public final int getTableLength() { 214 return line_number_table == null ? 0 : line_number_table.length; 215 } 216 } 217