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.DataInputStream;
21 import java.io.DataOutputStream;
22 import java.io.IOException;
23 import org.apache.bcel.Constants;
24
25 /**
26 * This class represents a table of line numbers for debugging
27 * purposes. This attribute is used by the <em>Code</em> attribute. It
28 * contains pairs of PCs and line numbers.
29 *
30 * @version $Id: LineNumberTable.java 1152077 2011-07-29 02:29:42Z dbrosius $
31 * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A>
32 * @see Code
33 * @see LineNumber
34 */
35 public final class LineNumberTable extends Attribute {
36
37 private static final long serialVersionUID = -6967221519632128904L;
38 private int line_number_table_length;
39 private LineNumber[] line_number_table; // Table of line/numbers pairs
40
41
42 /*
43 * Initialize from another object. Note that both objects use the same
44 * references (shallow copy). Use copy() for a physical copy.
45 */
46 public LineNumberTable(LineNumberTable c) {
47 this(c.getNameIndex(), c.getLength(), c.getLineNumberTable(), c.getConstantPool());
48 }
49
50
51 /*
52 * @param name_index Index of name
53 * @param length Content length in bytes
54 * @param line_number_table Table of line/numbers pairs
55 * @param constant_pool Array of constants
56 */
57 public LineNumberTable(int name_index, int length, LineNumber[] line_number_table,
58 ConstantPool constant_pool) {
59 super(Constants.ATTR_LINE_NUMBER_TABLE, name_index, length, constant_pool);
60 setLineNumberTable(line_number_table);
61 }
62
63
64 /**
65 * Construct object from file stream.
66 * @param name_index Index of name
67 * @param length Content length in bytes
68 * @param file Input stream
69 * @param constant_pool Array of constants
70 * @throws IOException
71 */
72 LineNumberTable(int name_index, int length, DataInputStream file, ConstantPool constant_pool)
73 throws IOException {
74 this(name_index, length, (LineNumber[]) null, constant_pool);
75 line_number_table_length = (file.readUnsignedShort());
76 line_number_table = new LineNumber[line_number_table_length];
77 for (int i = 0; i < line_number_table_length; i++) {
78 line_number_table[i] = new LineNumber(file);
79 }
80 }
81
82
83 /**
84 * Called by objects that are traversing the nodes of the tree implicitely
85 * defined by the contents of a Java class. I.e., the hierarchy of methods,
86 * fields, attributes, etc. spawns a tree of objects.
87 *
88 * @param v Visitor object
89 */
90 @Override
91 public void accept( Visitor v ) {
92 v.visitLineNumberTable(this);
93 }
94
95
96 /**
97 * Dump line number table attribute to file stream in binary format.
98 *
99 * @param file Output file stream
100 * @throws IOException
101 */
102 @Override
103 public final void dump( DataOutputStream file ) throws IOException {
104 super.dump(file);
105 file.writeShort(line_number_table_length);
106 for (int i = 0; i < line_number_table_length; i++) {
107 line_number_table[i].dump(file);
108 }
109 }
110
111
112 /**
113 * @return Array of (pc offset, line number) pairs.
114 */
115 public final LineNumber[] getLineNumberTable() {
116 return line_number_table;
117 }
118
119
120 /**
121 * @param line_number_table the line number entries for this table
122 */
123 public final void setLineNumberTable( LineNumber[] line_number_table ) {
124 this.line_number_table = line_number_table;
125 line_number_table_length = (line_number_table == null) ? 0 : line_number_table.length;
126 }
127
128
129 /**
130 * @return String representation.
131 */
132 @Override
133 public final String toString() {
134 StringBuilder buf = new StringBuilder();
135 StringBuilder line = new StringBuilder();
136 String newLine = System.getProperty("line.separator", "\n");
137 for (int i = 0; i < line_number_table_length; i++) {
138 line.append(line_number_table[i].toString());
139 if (i < line_number_table_length - 1) {
140 line.append(", ");
141 }
142 if (line.length() > 72) {
143 line.append(newLine);
144 buf.append(line.toString());
145 line.setLength(0);
146 }
147 }
148 buf.append(line);
149 return buf.toString();
150 }
151
152
153 /**
154 * Map byte code positions to source code lines.
155 *
156 * @param pos byte code offset
157 * @return corresponding line in source code
158 */
159 public int getSourceLine( int pos ) {
160 int l = 0, r = line_number_table_length - 1;
161 if (r < 0) {
162 return -1;
163 }
164 int min_index = -1, min = -1;
165 /* Do a binary search since the array is ordered.
166 */
167 do {
168 int i = (l + r) / 2;
169 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
200 public Attribute copy( ConstantPool _constant_pool ) {
201 LineNumberTable c = (LineNumberTable) clone();
202 c.line_number_table = new LineNumber[line_number_table_length];
203 for (int i = 0; i < line_number_table_length; i++) {
204 c.line_number_table[i] = line_number_table[i].copy();
205 }
206 c.constant_pool = _constant_pool;
207 return c;
208 }
209
210
211 public final int getTableLength() {
212 return line_number_table_length;
213 }
214 }