001 package edu.rice.cs.cunit.classFile.attributes;
002
003 import edu.rice.cs.cunit.classFile.attributes.visitors.IAttributeVisitor;
004 import edu.rice.cs.cunit.classFile.code.instructions.LineNumberTable;
005 import edu.rice.cs.cunit.classFile.constantPool.AUTFPoolInfo;
006 import edu.rice.cs.cunit.classFile.constantPool.ConstantPool;
007 import edu.rice.cs.cunit.util.Types;
008
009 /**
010 * Represents the LineNumberTable attribute in a class file.
011 *
012 * @author Mathias Ricken
013 */
014 public class LineNumberTableAttributeInfo extends AAttributeInfo {
015 /**
016 * Storage class for line number/program counter _pairs.
017 */
018 public static class LineNumberRecord {
019 public int startPC;
020 public int lineNo;
021
022 public LineNumberRecord(int startPC, int lineNo) {
023 assert(startPC <= 0xffff);
024 assert(lineNo <= 0xffff);
025 this.startPC = startPC;
026 this.lineNo = lineNo;
027 }
028 }
029
030 /**
031 * Constructor.
032 *
033 * @param name attribute name
034 * @param data attribute data
035 * @param cp constant pool
036 *
037 * @throws ClassFormatError
038 */
039 public LineNumberTableAttributeInfo(AUTFPoolInfo name, byte[] data, ConstantPool cp) throws ClassFormatError {
040 super(name, data, cp);
041 }
042
043 /**
044 * Return the number of line number records.
045 *
046 * @return number of line number records
047 *
048 * @throws ClassFormatError
049 */
050 public int getLineNumberCount() {
051 int res = Types.ushortFromBytes(_data, 0);
052 assert(res <= 0xffff);
053 return res;
054 }
055
056 /**
057 * Return a copy of the line number records.
058 *
059 * @return list of line number records
060 *
061 * @throws ClassFormatError
062 */
063 public LineNumberRecord[] getLineNumbers() throws ClassFormatError {
064 int count = getLineNumberCount();
065 LineNumberRecord[] lnr = new LineNumberRecord[count];
066 for(int i = 0; i < count; ++i) {
067 lnr[i] = new LineNumberRecord(Types.ushortFromBytes(_data, 2 + 4 * i),
068 Types.ushortFromBytes(_data, 4 + 4 * i));
069 }
070 return lnr;
071 }
072
073 /**
074 * Set the line number records.
075 *
076 * @param lnr list of line number records
077 */
078 public void setLineNumbers(LineNumberRecord[] lnr) {
079 byte[] newData = new byte[2 + 4 * lnr.length];
080 Types.bytesFromShort((short)lnr.length, newData, 0);
081 for(int i = 0; i < lnr.length; ++i) {
082 Types.bytesFromShort((short)(0xffff & lnr[i].startPC), newData, 2 + 4 * i);
083 Types.bytesFromShort((short)(0xffff & lnr[i].lineNo), newData, 4 + 4 * i);
084 }
085 setData(newData);
086 }
087
088 /**
089 * Execute a visitor on this attribute.
090 *
091 * @param visitor visitor
092 * @param param visitor-specific parameter
093 *
094 * @return visitor-specific return value
095 */
096 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) {
097 return visitor.lineNumberTableCase(this, param);
098 }
099
100 /**
101 * Return a human-readable version of this attribute.
102 *
103 * @return string
104 */
105 public String toString() {
106 StringBuilder x = new StringBuilder();
107 x.append(_name + " <" + getLineNumberCount() + " line numbers { ");
108 boolean first = true;
109 for(LineNumberRecord lnr : getLineNumbers()) {
110 if (first) {
111 first = false;
112 }
113 else {
114 x.append(", ");
115 }
116 x.append("(PC=" + lnr.startPC + " @ " + lnr.lineNo + ")");
117 }
118 x.append(" } >");
119 return x.toString();
120 }
121
122 /**
123 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them.
124 *
125 * @param startPC program counter to start at
126 * @param deltaPC change in program counter values
127 */
128 public void adjustPC(int startPC, int deltaPC) {
129 LineNumberRecord[] lnrs = getLineNumbers();
130 for(LineNumberRecord l : lnrs) {
131 if (l.startPC >= startPC) {
132 // Debug.out.println("Adjusted LineNumberTableAttribute.lnrs[].startPC: "+l.startPC+" --> "+(l.startPC+deltaPC));
133 l.startPC += deltaPC;
134 }
135 }
136 setLineNumbers(lnrs);
137 }
138
139 /**
140 * Translate the program counter values contained in this attribute from an old line number table to a new one.
141 *
142 * @param index critical point (insertion or deletion point)
143 * @param deltaIndex delta value to add to all old line numbers greater than the critical point
144 * @param oldLnt old line number table
145 * @param newLnt new line number table
146 */
147 public void translatePC(int index, int deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) {
148 LineNumberRecord[] lnrs = getLineNumbers();
149 for(LineNumberRecord l : lnrs) {
150 int oldLineNo = oldLnt.getLineNumber(l.startPC);
151 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
152 l.startPC = (short)newLnt.getPC(oldLineNo);
153 }
154 setLineNumbers(lnrs);
155 }
156
157 /**
158 * Creates and returns a copy of this object.
159 */
160 public Object clone() throws CloneNotSupportedException {
161 return super.clone();
162 }
163
164 /**
165 * Returns the name of the attribute as it appears in the class file.
166 *
167 * @return name of the attribute.
168 */
169 public static String getAttributeName() {
170 return "LineNumberTable";
171 }
172 }