1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.IOException;
20import java.util.Map;
21
22/**
23 * <code>LineNumberTable_attribute</code>.
24 */
25public class LineNumberAttribute extends AttributeInfo {
26    /**
27     * The name of this attribute <code>"LineNumberTable"</code>.
28     */
29    public static final String tag = "LineNumberTable";
30
31    LineNumberAttribute(ConstPool cp, int n, DataInputStream in)
32        throws IOException
33    {
34        super(cp, n, in);
35    }
36
37    private LineNumberAttribute(ConstPool cp, byte[] i) {
38        super(cp, tag, i);
39    }
40
41    /**
42     * Returns <code>line_number_table_length</code>.
43     * This represents the number of entries in the table.
44     */
45    public int tableLength() {
46        return ByteArray.readU16bit(info, 0);
47    }
48
49    /**
50     * Returns <code>line_number_table[i].start_pc</code>.
51     * This represents the index into the code array at which the code
52     * for a new line in the original source file begins.
53     *
54     * @param i         the i-th entry.
55     */
56    public int startPc(int i) {
57        return ByteArray.readU16bit(info, i * 4 + 2);
58    }
59
60    /**
61     * Returns <code>line_number_table[i].line_number</code>.
62     * This represents the corresponding line number in the original
63     * source file.
64     *
65     * @param i         the i-th entry.
66     */
67    public int lineNumber(int i) {
68        return ByteArray.readU16bit(info, i * 4 + 4);
69    }
70
71    /**
72     * Returns the line number corresponding to the specified bytecode.
73     *
74     * @param pc        the index into the code array.
75     */
76    public int toLineNumber(int pc) {
77        int n = tableLength();
78        int i = 0;
79        for (; i < n; ++i)
80            if (pc < startPc(i))
81                if (i == 0)
82                    return lineNumber(0);
83                else
84                    break;
85
86        return lineNumber(i - 1);
87    }
88
89    /**
90     * Returns the index into the code array at which the code for
91     * the specified line begins.
92     *
93     * @param line      the line number.
94     * @return          -1 if the specified line is not found.
95     */
96    public int toStartPc(int line) {
97        int n = tableLength();
98        for (int i = 0; i < n; ++i)
99            if (line == lineNumber(i))
100                return startPc(i);
101
102        return -1;
103    }
104
105    /**
106     * Used as a return type of <code>toNearPc()</code>.
107     */
108    static public class Pc {
109        /**
110         * The index into the code array.
111         */
112        public int index;
113        /**
114         * The line number.
115         */
116        public int line;
117    }
118
119    /**
120     * Returns the index into the code array at which the code for
121     * the specified line (or the nearest line after the specified one)
122     * begins.
123     *
124     * @param line      the line number.
125     * @return          a pair of the index and the line number of the
126     *                  bytecode at that index.
127     */
128    public Pc toNearPc(int line) {
129        int n = tableLength();
130        int nearPc = 0;
131        int distance = 0;
132        if (n > 0) {
133            distance = lineNumber(0) - line;
134            nearPc = startPc(0);
135        }
136
137        for (int i = 1; i < n; ++i) {
138            int d = lineNumber(i) - line;
139            if ((d < 0 && d > distance)
140                || (d >= 0 && (d < distance || distance < 0))) {
141                    distance = d;
142                    nearPc = startPc(i);
143            }
144        }
145
146        Pc res = new Pc();
147        res.index = nearPc;
148        res.line = line + distance;
149        return res;
150    }
151
152    /**
153     * Makes a copy.
154     *
155     * @param newCp     the constant pool table used by the new copy.
156     * @param classnames        should be null.
157     */
158    public AttributeInfo copy(ConstPool newCp, Map classnames) {
159        byte[] src = info;
160        int num = src.length;
161        byte[] dest = new byte[num];
162        for (int i = 0; i < num; ++i)
163            dest[i] = src[i];
164
165        LineNumberAttribute attr = new LineNumberAttribute(newCp, dest);
166        return attr;
167    }
168
169    /**
170     * Adjusts start_pc if bytecode is inserted in a method body.
171     */
172    void shiftPc(int where, int gapLength, boolean exclusive) {
173        int n = tableLength();
174        for (int i = 0; i < n; ++i) {
175            int pos = i * 4 + 2;
176            int pc = ByteArray.readU16bit(info, pos);
177            if (pc > where || (exclusive && pc == where))
178                ByteArray.write16bit(pc + gapLength, info, pos);
179        }
180    }
181}
182