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 }