View Javadoc

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 }