001 package serp.bytecode;
002
003 import java.io.*;
004 import java.util.*;
005
006 import serp.bytecode.visitor.*;
007
008 /**
009 * Code blocks compiled from source have line number tables mapping
010 * opcodes to source lines. This table automatically maintains line
011 * numbers in ascending order by their start program counter position
012 * at all times.
013 *
014 * @author Abe White
015 */
016 public class LineNumberTable extends Attribute implements InstructionPtr {
017 private List _lineNumbers = new ArrayList();
018
019 LineNumberTable(int nameIndex, Attributes owner) {
020 super(nameIndex, owner);
021 }
022
023 /**
024 * Return the line numbers held in this table.
025 */
026 public LineNumber[] getLineNumbers() {
027 Collections.sort(_lineNumbers);
028 return (LineNumber[]) _lineNumbers.toArray
029 (new LineNumber[_lineNumbers.size()]);
030 }
031
032 /**
033 * Return the line number for the given program counter, or null if none.
034 */
035 public LineNumber getLineNumber(int pc) {
036 for (int i = _lineNumbers.size() - 1; i >= 0; i--)
037 if (((LineNumber) _lineNumbers.get(i))._target.getByteIndex() <= pc)
038 return (LineNumber) _lineNumbers.get(i);
039 return null;
040 }
041
042 /**
043 * Return the line number for the given instruction, or null if none.
044 */
045 public LineNumber getLineNumber(Instruction ins) {
046 if (ins == null)
047 return null;
048 return getLineNumber(ins.getByteIndex());
049 }
050
051 /**
052 * Set the line numbers for the table. This method is useful when
053 * importing line numbers from another method.
054 */
055 public void setLineNumbers(LineNumber[] lines) {
056 clear();
057 if (lines != null)
058 for (int i = 0; i < lines.length; i++)
059 addLineNumber(lines[i]);
060 }
061
062 /**
063 * Import a line number from another method.
064 *
065 * @return the newly added line number
066 */
067 public LineNumber addLineNumber(LineNumber ln) {
068 LineNumber line = addLineNumber();
069 line.setStartPc(ln.getStartPc());
070 line.setLine(ln.getLine());
071 return line;
072 }
073
074 /**
075 * Add a new line number to this table.
076 */
077 public LineNumber addLineNumber() {
078 LineNumber ln = new LineNumber(this);
079 _lineNumbers.add(ln);
080 return ln;
081 }
082
083 /**
084 * Add a new line number to this table.
085 */
086 public LineNumber addLineNumber(int startPc, int line) {
087 LineNumber ln = addLineNumber();
088 ln.setStartPc(startPc);
089 ln.setLine(line);
090 return ln;
091 }
092
093 /**
094 * Add a new line number to this table.
095 */
096 public LineNumber addLineNumber(Instruction start, int line) {
097 LineNumber ln = addLineNumber();
098 ln.setStart(start);
099 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 }