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