001: // Copyright (c) 2004 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.bytecode;
005:
006: /**
007: * Represents the contents of a JSR-45 "SourceDebugExtension" attribute.
008: * We only support generating a single "stratum".
009: */
010:
011: public class SourceDebugExtAttr extends Attribute {
012: byte[] data;
013: int dlength;
014:
015: private String outputFileName;
016: private String defaultStratumId;
017:
018: /** Number of files in the <code>FileSection</code>. The active (used)
019: * length of the <code>fileNames</code> and <code>fileIDs</code> arrays. */
020: int fileCount;
021: /** For each entry in the <code>FileSection</code>:
022: * <code>2*id+with_path</code>.
023: * The <code>id</code> is the <code>FileID</code> written in the SMAP;
024: * the <code>with_path</code> is 1 if we also write the full path. */
025: int[] fileIDs;
026: /** For eachentry its <code>FileName</code>.
027: * If the <code>with_path</code> bit is set,
028: * <code>FileName '\n' AbsoluteFileName</code>. */
029: String[] fileNames;
030:
031: int lineCount;
032: /** A table of LineInfo entries. */
033: int[] lines;
034:
035: //int curMinLine; int curMaxLine; int curLineAdjust;
036: int curLineIndex = -1;
037: int curFileIndex = -1;
038: int maxFileID;
039: String curFileName;
040:
041: private int fixLine(int sourceLine, int index) {
042: int sourceMin = lines[index];
043: int repeat = lines[index + 2];
044: if (sourceLine < sourceMin) {
045: if (index > 0)
046: return -1;
047: int sourceMax = sourceMin + repeat - 1;
048: lines[index] = sourceLine;
049: lines[index + 2] = sourceMax - sourceLine + 1;
050: lines[index + 3] = sourceLine;
051: sourceMin = sourceLine;
052: }
053: int delta = lines[index + 3] - sourceMin;
054: if (sourceLine < sourceMin + repeat)
055: return sourceLine + delta;
056: else if (index == 5 * (lineCount - 1)
057: || (index == 0 && sourceLine < lines[5 + 3])) { // If last LineInfo entry, we can "extend" its range.
058: lines[index + 2] = sourceLine - sourceMin + 1; // Fix RepeatCount.
059: return sourceLine + delta;
060: }
061: return -1;
062: }
063:
064: int fixLine(int sourceLine) {
065: int outLine;
066: if (curLineIndex >= 0) {
067: outLine = fixLine(sourceLine, curLineIndex);
068: if (outLine >= 0)
069: return outLine;
070: }
071: int i5 = 0;
072: int findex = curFileIndex;
073: for (int i = 0; i < lineCount; i++) {
074: if (i5 != curLineIndex && findex == lines[i5 + 1]) {
075: outLine = fixLine(sourceLine, i5);
076: if (outLine >= 0) {
077: curLineIndex = i5;
078: return outLine;
079: }
080: }
081: i5 += 5;
082: }
083: if (lines == null)
084: lines = new int[20];
085: else if (i5 >= lines.length) {
086: int[] newLines = new int[2 * i5];
087: System.arraycopy(lines, 0, newLines, 0, i5);
088: lines = newLines;
089: }
090: int outputStartLine;
091: int inputStartLine = sourceLine;
092: if (i5 == 0)
093: outputStartLine = sourceLine;
094: else {
095: outputStartLine = lines[i5 - 5 + 3] + lines[i5 - 5 + 2];
096: if (i5 == 5 && outputStartLine < 10000) {
097: // Reserve the first 10k lines for the main file.
098: // This is for the sake of non-JSR-45-capable tools.
099: outputStartLine = 10000;
100: }
101: sourceLine = outputStartLine;
102: }
103: lines[i5] = inputStartLine;
104: lines[i5 + 1] = findex;
105: lines[i5 + 2] = 1;
106: lines[i5 + 3] = outputStartLine;
107: lines[i5 + 4] = 1;
108: curLineIndex = i5;
109: lineCount++;
110: return sourceLine;
111: }
112:
113: void addFile(String fname) {
114: if (curFileName == fname
115: || (fname != null && fname.equals(curFileName)))
116: return;
117: curFileName = fname;
118: fname = SourceFileAttr.fixSourceFile(fname);
119: String fentry;
120: int slash = fname.lastIndexOf('/');
121: if (slash >= 0) {
122: String fpath = fname;
123: fname = fname.substring(slash + 1);
124: fentry = fname + '\n' + fpath;
125: } else
126: fentry = fname;
127:
128: if (curFileIndex >= 0 && fentry.equals(fileNames[curFileIndex]))
129: return;
130:
131: int n = fileCount;
132: for (int i = 0; i < n; i++) {
133: if (i != curFileIndex && fentry.equals(fileNames[i])) {
134: curFileIndex = i;
135: curLineIndex = -1;
136: return;
137: }
138: }
139:
140: if (fileIDs == null) {
141: fileIDs = new int[5];
142: fileNames = new String[5];
143: } else if (n >= fileIDs.length) {
144: int[] newIDs = new int[2 * n];
145: String[] newNames = new String[2 * n];
146: System.arraycopy(fileIDs, 0, newIDs, 0, n);
147: System.arraycopy(fileNames, 0, newNames, 0, n);
148: fileIDs = newIDs;
149: fileNames = newNames;
150: }
151:
152: fileCount++;
153: int id = ++maxFileID;
154: id = id << 1;
155: if (slash >= 0)
156: id++;
157: fileNames[n] = fentry;
158: if (outputFileName == null)
159: outputFileName = fname;
160: fileIDs[n] = id;
161: curFileIndex = n;
162: curLineIndex = -1;
163: }
164:
165: public void addStratum(String name) {
166: defaultStratumId = name;
167: }
168:
169: /** Add a new InnerClassesAttr to a ClassType. */
170: public SourceDebugExtAttr(ClassType cl) {
171: super ("SourceDebugExtension");
172: addToFrontOf(cl);
173: }
174:
175: void nonAsteriskString(String str, StringBuffer sbuf) {
176: if (str == null || str.length() == 0 || str.charAt(0) == '*')
177: sbuf.append(' ');
178: sbuf.append(str);
179: }
180:
181: public void assignConstants(ClassType cl) {
182: super .assignConstants(cl);
183:
184: StringBuffer sbuf = new StringBuffer();
185: // Append SMAP Header:
186: sbuf.append("SMAP\n");
187: nonAsteriskString(outputFileName, sbuf);
188: sbuf.append('\n');
189: String stratum = defaultStratumId == null ? "Java"
190: : defaultStratumId;
191: nonAsteriskString(stratum, sbuf);
192: sbuf.append('\n');
193: // Append StratumSection.
194: sbuf.append("*S ");
195: sbuf.append(stratum);
196: sbuf.append('\n');
197: // Append FileSection:
198: sbuf.append("*F\n");
199: for (int i = 0; i < fileCount; i++) {
200: int id = fileIDs[i];
201: boolean with_path = (id & 1) != 0;
202: id >>= 1;
203: if (with_path)
204: sbuf.append("+ ");
205: sbuf.append(id);
206: sbuf.append(' ');
207: sbuf.append(fileNames[i]);
208: sbuf.append('\n');
209: }
210: // Append LineSection:
211: if (lineCount > 0) {
212: int prevFileID = 0;
213: sbuf.append("*L\n");
214: int i = 0, i5 = 0;
215: do {
216: int inputStartLine = lines[i5];
217: int lineFileID = fileIDs[lines[i5 + 1]] >> 1;
218: int repeatCount = lines[i5 + 2];
219: int outputStartLine = lines[i5 + 3];
220: int outputLineIncrement = lines[i5 + 4];
221: sbuf.append(inputStartLine);
222: if (lineFileID != prevFileID) {
223: sbuf.append('#');
224: sbuf.append(lineFileID);
225: prevFileID = lineFileID;
226: }
227: if (repeatCount != 1) {
228: sbuf.append(',');
229: sbuf.append(repeatCount);
230: }
231: sbuf.append(':');
232: sbuf.append(outputStartLine);
233: if (outputLineIncrement != 1) {
234: sbuf.append(',');
235: sbuf.append(outputLineIncrement);
236: }
237: sbuf.append('\n');
238: i5 += 5;
239: } while (++i < lineCount);
240: }
241: // Append EndSection:
242: sbuf.append("*E\n");
243: try {
244: data = sbuf.toString().getBytes("UTF-8");
245: } catch (Exception ex) {
246: throw new RuntimeException(ex.toString());
247: }
248: dlength = data.length;
249: }
250:
251: /** Return the length of the attribute in bytes.
252: * Does not include the 6-byte header (for the name_index and the length).*/
253: public int getLength() {
254: return dlength;
255: }
256:
257: /** Write out the contents of the Attribute.
258: * Does not write the 6-byte attribute header. */
259: public void write(java.io.DataOutputStream dstr)
260: throws java.io.IOException {
261: dstr.write(data, 0, dlength);
262: }
263:
264: public void print(ClassTypeWriter dst) {
265: dst.print("Attribute \"");
266: dst.print(getName());
267: dst.print("\", length:");
268: dst.println(dlength);
269: try {
270: dst.print(new String(data, 0, dlength, "UTF-8"));
271: } catch (Exception ex) {
272: dst.print("(Caught ");
273: dst.print(ex);
274: dst.println(')');
275: }
276: if (dlength > 0 && data[dlength - 1] != '\r'
277: && data[dlength - 1] != '\n')
278: dst.println();
279: }
280: }
|