1 package serp.bytecode;
2
3 import java.io.*;
4 import java.util.*;
5
6 import serp.bytecode.visitor.*;
7
8 /***
9 * Code blocks compiled from source have line number tables mapping
10 * opcodes to source lines. This table automatically maintains line
11 * numbers in ascending order by their start program counter position
12 * at all times.
13 *
14 * @author Abe White
15 */
16 public class LineNumberTable extends Attribute implements InstructionPtr {
17 private List _lineNumbers = new ArrayList();
18
19 LineNumberTable(int nameIndex, Attributes owner) {
20 super(nameIndex, owner);
21 }
22
23 /***
24 * Return the line numbers held in this table.
25 */
26 public LineNumber[] getLineNumbers() {
27 Collections.sort(_lineNumbers);
28 return (LineNumber[]) _lineNumbers.toArray
29 (new LineNumber[_lineNumbers.size()]);
30 }
31
32 /***
33 * Return the line number for the given program counter, or null if none.
34 */
35 public LineNumber getLineNumber(int pc) {
36 for (int i = _lineNumbers.size() - 1; i >= 0; i--)
37 if (((LineNumber) _lineNumbers.get(i))._target.getByteIndex() <= pc)
38 return (LineNumber) _lineNumbers.get(i);
39 return null;
40 }
41
42 /***
43 * Return the line number for the given instruction, or null if none.
44 */
45 public LineNumber getLineNumber(Instruction ins) {
46 if (ins == null)
47 return null;
48 return getLineNumber(ins.getByteIndex());
49 }
50
51 /***
52 * Set the line numbers for the table. This method is useful when
53 * importing line numbers from another method.
54 */
55 public void setLineNumbers(LineNumber[] lines) {
56 clear();
57 if (lines != null)
58 for (int i = 0; i < lines.length; i++)
59 addLineNumber(lines[i]);
60 }
61
62 /***
63 * Import a line number from another method.
64 *
65 * @return the newly added line number
66 */
67 public LineNumber addLineNumber(LineNumber ln) {
68 LineNumber line = addLineNumber();
69 line.setStartPc(ln.getStartPc());
70 line.setLine(ln.getLine());
71 return line;
72 }
73
74 /***
75 * Add a new line number to this table.
76 */
77 public LineNumber addLineNumber() {
78 LineNumber ln = new LineNumber(this);
79 _lineNumbers.add(ln);
80 return ln;
81 }
82
83 /***
84 * Add a new line number to this table.
85 */
86 public LineNumber addLineNumber(int startPc, int line) {
87 LineNumber ln = addLineNumber();
88 ln.setStartPc(startPc);
89 ln.setLine(line);
90 return ln;
91 }
92
93 /***
94 * Add a new line number to this table.
95 */
96 public LineNumber addLineNumber(Instruction start, int line) {
97 LineNumber ln = addLineNumber();
98 ln.setStart(start);
99 ln.setLine(line);
100 return ln;
101 }
102
103 /***
104 * Clear the line numbers.
105 */
106 public void clear() {
107 for (int i = 0; i < _lineNumbers.size(); i++)
108 ((LineNumber) _lineNumbers.get(i)).invalidate();
109 _lineNumbers.clear();
110 }
111
112 /***
113 * Remove the given line.
114 *
115 * @return true if the line was removed, false otherwise
116 */
117 public boolean removeLineNumber(LineNumber ln) {
118 if (ln == null || !_lineNumbers.remove(ln))
119 return false;
120 ln.invalidate();
121 return true;
122 }
123
124 /***
125 * Remove the line number for the given program counter.
126 *
127 * @return true if the line was removed, false otherwise
128 */
129 public boolean removeLineNumber(int pc) {
130 return removeLineNumber(getLineNumber(pc));
131 }
132
133 /***
134 * Remove the line number for the given instruction.
135 *
136 * @return true if the line was removed, false otherwise
137 */
138 public boolean removeLineNumber(Instruction ins) {
139 return removeLineNumber(getLineNumber(ins));
140 }
141
142 public void updateTargets() {
143 for (int i = 0; i < _lineNumbers.size(); i++)
144 ((LineNumber) _lineNumbers.get(i)).updateTargets();
145 }
146
147 public void replaceTarget(Instruction oldTarget, Instruction newTarget) {
148 for (int i = 0; i < _lineNumbers.size(); i++)
149 ((LineNumber) _lineNumbers.get(i)).replaceTarget(oldTarget,
150 newTarget);
151 }
152
153 public void acceptVisit(BCVisitor visit) {
154 visit.enterLineNumberTable(this);
155 LineNumber[] lines = getLineNumbers();
156 for (int i = 0; i < lines.length; i++)
157 lines[i].acceptVisit(visit);
158 visit.exitLineNumberTable(this);
159 }
160
161 int getLength() {
162 return 2 + (4 * _lineNumbers.size());
163 }
164
165 void read(Attribute other) {
166 setLineNumbers(((LineNumberTable) other).getLineNumbers());
167 }
168
169 void read(DataInput in, int length) throws IOException {
170 clear();
171 int numLines = in.readUnsignedShort();
172 LineNumber lineNumber;
173 for (int i = 0; i < numLines; i++) {
174 lineNumber = addLineNumber();
175 lineNumber.read(in);
176 }
177 }
178
179 void write(DataOutput out, int length) throws IOException {
180 LineNumber[] lines = getLineNumbers();
181 out.writeShort(lines.length);
182 for (int i = 0; i < lines.length; i++)
183 lines[i].write(out);
184 }
185
186 public Code getCode() {
187 return (Code) getOwner();
188 }
189 }