View Javadoc
1   /*
2    * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   *
23   */
24  
25  package sun.jvm.hotspot.jdi;
26  
27  import com.sun.jdi.*;
28  
29  import java.util.*;
30  import java.io.File;
31  
32  class SDE {
33      private static final int INIT_SIZE_FILE = 3;
34      private static final int INIT_SIZE_LINE = 100;
35      private static final int INIT_SIZE_STRATUM = 3;
36  
37      static final String BASE_STRATUM_NAME = "Java";
38  
39      /* for C capatibility */
40      static final String NullString = null;
41  
42      private class FileTableRecord {
43          int fileId;
44          String sourceName;
45          String sourcePath; // do not read - use accessor
46          boolean isConverted = false;
47  
48          /**
49           * Return the sourcePath, computing it if not set.
50           * If set, convert '/' in the sourcePath to the
51           * local file separator.
52           */
53          String getSourcePath(ReferenceTypeImpl refType) {
54              if (!isConverted) {
55                  if (sourcePath == null) {
56                      sourcePath = refType.baseSourceDir() + sourceName;
57                  } else {
58                      StringBuffer buf = new StringBuffer();
59                      for (int i = 0; i < sourcePath.length(); ++i) {
60                          char ch = sourcePath.charAt(i);
61                          if (ch == '/') {
62                              buf.append(File.separatorChar);
63                          } else {
64                              buf.append(ch);
65                          }
66                      }
67                      sourcePath = buf.toString();
68                  }
69                  isConverted = true;
70              }
71              return sourcePath;
72          }
73      }
74  
75      private class LineTableRecord {
76          int jplsStart;
77          int jplsEnd;
78          int jplsLineInc;
79          int njplsStart;
80          int njplsEnd;
81          int fileId;
82      }
83  
84      private class StratumTableRecord {
85          String id;
86          int fileIndex;
87          int lineIndex;
88      }
89  
90      class Stratum {
91          private final int sti; /* stratum index */
92  
93          private Stratum(int sti) {
94              this.sti = sti;
95          }
96  
97          String id() {
98              return stratumTable[sti].id;
99          }
100 
101         boolean isJava() {
102             return sti == baseStratumIndex;
103         }
104 
105         /**
106          * Return all the sourceNames for this stratum.
107          * Look from our starting fileIndex upto the starting
108          * fileIndex of next stratum - can do this since there
109          * is always a terminator stratum.
110          * Default sourceName (the first one) must be first.
111          */
112         List sourceNames(ReferenceTypeImpl refType) {
113             int i;
114             int fileIndexStart = stratumTable[sti].fileIndex;
115             /* one past end */
116             int fileIndexEnd = stratumTable[sti+1].fileIndex;
117             List result = new ArrayList(fileIndexEnd - fileIndexStart);
118             for (i = fileIndexStart; i < fileIndexEnd; ++i) {
119                 result.add(fileTable[i].sourceName);
120             }
121             return result;
122         }
123 
124         /**
125          * Return all the sourcePaths for this stratum.
126          * Look from our starting fileIndex upto the starting
127          * fileIndex of next stratum - can do this since there
128          * is always a terminator stratum.
129          * Default sourcePath (the first one) must be first.
130          */
131         List sourcePaths(ReferenceTypeImpl refType) {
132             int i;
133             int fileIndexStart = stratumTable[sti].fileIndex;
134             /* one past end */
135             int fileIndexEnd = stratumTable[sti+1].fileIndex;
136             List result = new ArrayList(fileIndexEnd - fileIndexStart);
137             for (i = fileIndexStart; i < fileIndexEnd; ++i) {
138                 result.add(fileTable[i].getSourcePath(refType));
139             }
140             return result;
141         }
142 
143         LineStratum lineStratum(ReferenceTypeImpl refType,
144                                 int jplsLine) {
145             int lti = stiLineTableIndex(sti, jplsLine);
146             if (lti < 0) {
147                 return null;
148             } else {
149                 return new LineStratum(sti, lti, refType,
150                                        jplsLine);
151             }
152         }
153     }
154 
155     class LineStratum {
156         private final int sti; /* stratum index */
157         private final int lti; /* line table index */
158         private final ReferenceTypeImpl refType;
159         private final int jplsLine;
160         private String sourceName = null;
161         private String sourcePath = null;
162 
163         private LineStratum(int sti, int lti,
164                             ReferenceTypeImpl refType,
165                             int jplsLine) {
166             this.sti = sti;
167             this.lti = lti;
168             this.refType = refType;
169             this.jplsLine = jplsLine;
170         }
171 
172         public boolean equals(Object obj) {
173             if ((obj != null) && (obj instanceof LineStratum)) {
174                 LineStratum other = (LineStratum)obj;
175                 return (lti == other.lti) &&
176                        (sti == other.sti) &&
177                        (lineNumber() == other.lineNumber()) &&
178                        (refType.equals(other.refType));
179             } else {
180                 return false;
181             }
182         }
183 
184         int lineNumber() {
185             return stiLineNumber(sti, lti, jplsLine);
186         }
187 
188         /**
189          * Fetch the source name and source path for
190          * this line, converting or constructing
191          * the source path if needed.
192          */
193         void getSourceInfo() {
194             if (sourceName != null) {
195                 // already done
196                 return;
197             }
198             int fti = stiFileTableIndex(sti, lti);
199             if (fti == -1) {
200                 throw new InternalError(
201               "Bad SourceDebugExtension, no matching source id " +
202               lineTable[lti].fileId + " jplsLine: " + jplsLine);
203             }
204             FileTableRecord ftr = fileTable[fti];
205             sourceName = ftr.sourceName;
206             sourcePath = ftr.getSourcePath(refType);
207         }
208 
209         String sourceName() {
210             getSourceInfo();
211             return sourceName;
212         }
213 
214         String sourcePath() {
215             getSourceInfo();
216             return sourcePath;
217         }
218     }
219 
220     private FileTableRecord[] fileTable = null;
221     private LineTableRecord[] lineTable = null;
222     private StratumTableRecord[] stratumTable = null;
223 
224     private int fileIndex = 0;
225     private int lineIndex = 0;
226     private int stratumIndex = 0;
227     private int currentFileId = 0;
228 
229     private int defaultStratumIndex = -1;
230     private int baseStratumIndex = -2; /* so as not to match -1 above */
231     private int sdePos = 0;
232 
233     final String sourceDebugExtension;
234     String jplsFilename = null;
235     String defaultStratumId = null;
236     boolean isValid = false;
237 
238     SDE(String sourceDebugExtension) {
239         this.sourceDebugExtension = sourceDebugExtension;
240         decode();
241     }
242 
243     SDE() {
244         this.sourceDebugExtension = null;
245         createProxyForAbsentSDE();
246     }
247 
248     char sdePeek() {
249         if (sdePos >= sourceDebugExtension.length()) {
250             syntax();
251         }
252         return sourceDebugExtension.charAt(sdePos);
253     }
254 
255     char sdeRead() {
256         if (sdePos >= sourceDebugExtension.length()) {
257             syntax();
258         }
259         return sourceDebugExtension.charAt(sdePos++);
260     }
261 
262     void sdeAdvance() {
263         sdePos++;
264     }
265 
266     void syntax() {
267         throw new InternalError("bad SourceDebugExtension syntax - position " +
268                                 sdePos);
269     }
270 
271     void syntax(String msg) {
272         throw new InternalError("bad SourceDebugExtension syntax: " + msg);
273     }
274 
275     void assureLineTableSize() {
276         int len = lineTable == null? 0 : lineTable.length;
277         if (lineIndex >= len) {
278             int i;
279             int newLen = len == 0? INIT_SIZE_LINE : len * 2;
280             LineTableRecord[] newTable = new LineTableRecord[newLen];
281             for (i = 0; i < len; ++i) {
282                 newTable[i] = lineTable[i];
283             }
284             for (; i < newLen; ++i) {
285                 newTable[i] = new LineTableRecord();
286             }
287             lineTable = newTable;
288         }
289     }
290 
291     void assureFileTableSize() {
292         int len = fileTable == null? 0 : fileTable.length;
293         if (fileIndex >= len) {
294             int i;
295             int newLen = len == 0? INIT_SIZE_FILE : len * 2;
296             FileTableRecord[] newTable = new FileTableRecord[newLen];
297             for (i = 0; i < len; ++i) {
298                 newTable[i] = fileTable[i];
299             }
300             for (; i < newLen; ++i) {
301                 newTable[i] = new FileTableRecord();
302             }
303             fileTable = newTable;
304         }
305     }
306 
307     void assureStratumTableSize() {
308         int len = stratumTable == null? 0 : stratumTable.length;
309         if (stratumIndex >= len) {
310             int i;
311             int newLen = len == 0? INIT_SIZE_STRATUM : len * 2;
312             StratumTableRecord[] newTable = new StratumTableRecord[newLen];
313             for (i = 0; i < len; ++i) {
314                 newTable[i] = stratumTable[i];
315             }
316             for (; i < newLen; ++i) {
317                 newTable[i] = new StratumTableRecord();
318             }
319             stratumTable = newTable;
320         }
321     }
322 
323     String readLine() {
324         StringBuffer sb = new StringBuffer();
325         char ch;
326 
327         ignoreWhite();
328         while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
329             sb.append((char)ch);
330         }
331         // check for CR LF
332         if ((ch == '\r') && (sdePeek() == '\n')) {
333             sdeRead();
334         }
335         ignoreWhite(); // leading white
336         return sb.toString();
337     }
338 
339     private int defaultStratumTableIndex() {
340         if ((defaultStratumIndex == -1) && (defaultStratumId != null)) {
341             defaultStratumIndex =
342                 stratumTableIndex(defaultStratumId);
343         }
344         return defaultStratumIndex;
345     }
346 
347     int stratumTableIndex(String stratumId) {
348         int i;
349 
350         if (stratumId == null) {
351             return defaultStratumTableIndex();
352         }
353         for (i = 0; i < (stratumIndex-1); ++i) {
354             if (stratumTable[i].id.equals(stratumId)) {
355                 return i;
356             }
357         }
358         return defaultStratumTableIndex();
359     }
360 
361     Stratum stratum(String stratumID) {
362         int sti = stratumTableIndex(stratumID);
363         return new Stratum(sti);
364     }
365 
366     List availableStrata() {
367         List strata = new ArrayList();
368 
369         for (int i = 0; i < (stratumIndex-1); ++i) {
370             StratumTableRecord rec = stratumTable[i];
371             strata.add(rec.id);
372         }
373         return strata;
374     }
375 
376 /*****************************
377  * below functions/methods are written to compile under either Java or C
378  *
379  * Needed support functions:
380  *   sdePeek()
381  *   sdeRead()
382  *   sdeAdvance()
383  *   readLine()
384  *   assureLineTableSize()
385  *   assureFileTableSize()
386  *   assureStratumTableSize()
387  *   syntax()
388  *
389  *   stratumTableIndex(String)
390  *
391  * Needed support variables:
392  *   lineTable
393  *   lineIndex
394  *   fileTable
395  *   fileIndex
396  *   currentFileId
397  *
398  * Needed types:
399  *   String
400  *
401  * Needed constants:
402  *   NullString
403  */
404 
405     void ignoreWhite() {
406         char ch;
407 
408         while (((ch = sdePeek()) == ' ') || (ch == '\t')) {
409             sdeAdvance();
410         }
411     }
412 
413     void ignoreLine() {
414         char ch;
415 
416         while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
417         }
418         /* check for CR LF */
419         if ((ch == '\r') && (sdePeek() == '\n')) {
420             sdeAdvance();
421         }
422         ignoreWhite(); /* leading white */
423     }
424 
425     int readNumber() {
426         int value = 0;
427         char ch;
428 
429         ignoreWhite();
430         while (((ch = sdePeek()) >= '0') && (ch <= '9')) {
431             sdeAdvance();
432             value = (value * 10) + ch - '0';
433         }
434         ignoreWhite();
435         return value;
436     }
437 
438     void storeFile(int fileId, String sourceName, String sourcePath) {
439         assureFileTableSize();
440         fileTable[fileIndex].fileId = fileId;
441         fileTable[fileIndex].sourceName = sourceName;
442         fileTable[fileIndex].sourcePath = sourcePath;
443         ++fileIndex;
444     }
445 
446     void fileLine() {
447         int hasAbsolute = 0; /* acts as boolean */
448         int fileId;
449         String sourceName;
450         String sourcePath = null;
451 
452         /* is there an absolute filename? */
453         if (sdePeek() == '+') {
454             sdeAdvance();
455             hasAbsolute = 1;
456         }
457         fileId = readNumber();
458         sourceName = readLine();
459         if (hasAbsolute == 1) {
460             sourcePath = readLine();
461         }
462 
463         storeFile(fileId, sourceName, sourcePath);
464     }
465 
466     void storeLine(int jplsStart, int jplsEnd, int jplsLineInc,
467                   int njplsStart, int njplsEnd, int fileId) {
468         assureLineTableSize();
469         lineTable[lineIndex].jplsStart = jplsStart;
470         lineTable[lineIndex].jplsEnd = jplsEnd;
471         lineTable[lineIndex].jplsLineInc = jplsLineInc;
472         lineTable[lineIndex].njplsStart = njplsStart;
473         lineTable[lineIndex].njplsEnd = njplsEnd;
474         lineTable[lineIndex].fileId = fileId;
475         ++lineIndex;
476     }
477 
478     /**
479      * Parse line translation info.  Syntax is
480      *     <NJ-start-line> [ # <file-id> ] [ , <line-count> ] :
481      *                 <J-start-line> [ , <line-increment> ] CR
482      */
483     void lineLine() {
484         int lineCount = 1;
485         int lineIncrement = 1;
486         int njplsStart;
487         int jplsStart;
488 
489         njplsStart = readNumber();
490 
491         /* is there a fileID? */
492         if (sdePeek() == '#') {
493             sdeAdvance();
494             currentFileId = readNumber();
495         }
496 
497         /* is there a line count? */
498         if (sdePeek() == ',') {
499             sdeAdvance();
500             lineCount = readNumber();
501         }
502 
503         if (sdeRead() != ':') {
504             syntax();
505         }
506         jplsStart = readNumber();
507         if (sdePeek() == ',') {
508             sdeAdvance();
509             lineIncrement = readNumber();
510         }
511         ignoreLine(); /* flush the rest */
512 
513         storeLine(jplsStart,
514                   jplsStart + (lineCount * lineIncrement) -1,
515                   lineIncrement,
516                   njplsStart,
517                   njplsStart + lineCount -1,
518                   currentFileId);
519     }
520 
521     /**
522      * Until the next stratum section, everything after this
523      * is in stratumId - so, store the current indicies.
524      */
525     void storeStratum(String stratumId) {
526         /* remove redundant strata */
527         if (stratumIndex > 0) {
528             if ((stratumTable[stratumIndex-1].fileIndex
529                                             == fileIndex) &&
530                 (stratumTable[stratumIndex-1].lineIndex
531                                             == lineIndex)) {
532                 /* nothing changed overwrite it */
533                 --stratumIndex;
534             }
535         }
536         /* store the results */
537         assureStratumTableSize();
538         stratumTable[stratumIndex].id = stratumId;
539         stratumTable[stratumIndex].fileIndex = fileIndex;
540         stratumTable[stratumIndex].lineIndex = lineIndex;
541         ++stratumIndex;
542         currentFileId = 0;
543     }
544 
545     /**
546      * The beginning of a stratum's info
547      */
548     void stratumSection() {
549         storeStratum(readLine());
550     }
551 
552     void fileSection() {
553         ignoreLine();
554         while (sdePeek() != '*') {
555             fileLine();
556         }
557     }
558 
559     void lineSection() {
560         ignoreLine();
561         while (sdePeek() != '*') {
562             lineLine();
563         }
564     }
565 
566     /**
567      * Ignore a section we don't know about.
568      */
569     void ignoreSection() {
570         ignoreLine();
571         while (sdePeek() != '*') {
572             ignoreLine();
573         }
574     }
575 
576     /**
577      * A base "Java" stratum is always available, though
578      * it is not in the SourceDebugExtension.
579      * Create the base stratum.
580      */
581     void createJavaStratum() {
582         baseStratumIndex = stratumIndex;
583         storeStratum(BASE_STRATUM_NAME);
584         storeFile(1, jplsFilename, NullString);
585         /* JPL line numbers cannot exceed 65535 */
586         storeLine(1, 65536, 1, 1, 65536, 1);
587         storeStratum("Aux"); /* in case they don't declare */
588     }
589 
590     /**
591      * Decode a SourceDebugExtension which is in SourceMap format.
592      * This is the entry point into the recursive descent parser.
593      */
594     void decode() {
595         /* check for "SMAP" - allow EOF if not ours */
596         if ((sourceDebugExtension.length() < 4) ||
597             (sdeRead() != 'S') ||
598             (sdeRead() != 'M') ||
599             (sdeRead() != 'A') ||
600             (sdeRead() != 'P')) {
601             return; /* not our info */
602         }
603         ignoreLine(); /* flush the rest */
604         jplsFilename = readLine();
605         defaultStratumId = readLine();
606         createJavaStratum();
607         while (true) {
608             if (sdeRead() != '*') {
609                 syntax();
610             }
611             switch (sdeRead()) {
612                 case 'S':
613                     stratumSection();
614                     break;
615                 case 'F':
616                     fileSection();
617                     break;
618                 case 'L':
619                     lineSection();
620                     break;
621                 case 'E':
622                     /* set end points */
623                     storeStratum("*terminator*");
624                     isValid = true;
625                     return;
626                 default:
627                     ignoreSection();
628             }
629         }
630     }
631 
632     void createProxyForAbsentSDE() {
633         jplsFilename = null;
634         defaultStratumId = BASE_STRATUM_NAME;
635         defaultStratumIndex = stratumIndex;
636         createJavaStratum();
637         storeStratum("*terminator*");
638     }
639 
640     /***************** query functions ***********************/
641 
642     private int stiLineTableIndex(int sti, int jplsLine) {
643         int i;
644         int lineIndexStart;
645         int lineIndexEnd;
646 
647         lineIndexStart = stratumTable[sti].lineIndex;
648         /* one past end */
649         lineIndexEnd = stratumTable[sti+1].lineIndex;
650         for (i = lineIndexStart; i < lineIndexEnd; ++i) {
651             if ((jplsLine >= lineTable[i].jplsStart) &&
652                             (jplsLine <= lineTable[i].jplsEnd)) {
653                 return i;
654             }
655         }
656         return -1;
657     }
658 
659     private int stiLineNumber(int sti, int lti, int jplsLine) {
660         return lineTable[lti].njplsStart +
661                 (((jplsLine - lineTable[lti].jplsStart) /
662                                    lineTable[lti].jplsLineInc));
663     }
664 
665     private int fileTableIndex(int sti, int fileId) {
666         int i;
667         int fileIndexStart = stratumTable[sti].fileIndex;
668         /* one past end */
669         int fileIndexEnd = stratumTable[sti+1].fileIndex;
670         for (i = fileIndexStart; i < fileIndexEnd; ++i) {
671             if (fileTable[i].fileId == fileId) {
672                 return i;
673             }
674         }
675         return -1;
676     }
677 
678     private int stiFileTableIndex(int sti, int lti) {
679         return fileTableIndex(sti, lineTable[lti].fileId);
680     }
681 
682     boolean isValid() {
683         return isValid;
684     }
685 }