Home | History | Annotate | Download | only in jdi
      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 }
    692