1 /*
   2  * Copyright (c) 2001, 2008, 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 != null) && (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         int lineNumber() {
 186             return stiLineNumber(sti, lti, jplsLine);
 187         }
 188 
 189         /**
 190          * Fetch the source name and source path for
 191          * this line, converting or constructing
 192          * the source path if needed.
 193          */
 194         void getSourceInfo() {
 195             if (sourceName != null) {
 196                 // already done
 197                 return;
 198             }
 199             int fti = stiFileTableIndex(sti, lti);
 200             if (fti == -1) {
 201                 throw new InternalError(
 202               "Bad SourceDebugExtension, no matching source id " +
 203               lineTable[lti].fileId + " jplsLine: " + jplsLine);
 204             }
 205             FileTableRecord ftr = fileTable[fti];
 206             sourceName = ftr.sourceName;
 207             sourcePath = ftr.getSourcePath(refType);
 208         }
 209 
 210         String sourceName() {
 211             getSourceInfo();
 212             return sourceName;
 213         }
 214 
 215         String sourcePath() {
 216             getSourceInfo();
 217             return sourcePath;
 218         }
 219     }
 220 
 221     private FileTableRecord[] fileTable = null;
 222     private LineTableRecord[] lineTable = null;
 223     private StratumTableRecord[] stratumTable = null;
 224 
 225     private int fileIndex = 0;
 226     private int lineIndex = 0;
 227     private int stratumIndex = 0;
 228     private int currentFileId = 0;
 229 
 230     private int defaultStratumIndex = -1;
 231     private int baseStratumIndex = -2; /* so as not to match -1 above */
 232     private int sdePos = 0;
 233 
 234     final String sourceDebugExtension;
 235     String jplsFilename = null;
 236     String defaultStratumId = null;
 237     boolean isValid = false;
 238 
 239     SDE(String sourceDebugExtension) {
 240         this.sourceDebugExtension = sourceDebugExtension;
 241         decode();
 242     }
 243 
 244     SDE() {
 245         this.sourceDebugExtension = null;
 246         createProxyForAbsentSDE();
 247     }
 248 
 249     char sdePeek() {
 250         if (sdePos >= sourceDebugExtension.length()) {
 251             syntax();
 252         }
 253         return sourceDebugExtension.charAt(sdePos);
 254     }
 255 
 256     char sdeRead() {
 257         if (sdePos >= sourceDebugExtension.length()) {
 258             syntax();
 259         }
 260         return sourceDebugExtension.charAt(sdePos++);
 261     }
 262 
 263     void sdeAdvance() {
 264         sdePos++;
 265     }
 266 
 267     void syntax() {
 268         throw new InternalError("bad SourceDebugExtension syntax - position " +
 269                                 sdePos);
 270     }
 271 
 272     void syntax(String msg) {
 273         throw new InternalError("bad SourceDebugExtension syntax: " + msg);
 274     }
 275 
 276     void assureLineTableSize() {
 277         int len = lineTable == null? 0 : lineTable.length;
 278         if (lineIndex >= len) {
 279             int i;
 280             int newLen = len == 0? INIT_SIZE_LINE : len * 2;
 281             LineTableRecord[] newTable = new LineTableRecord[newLen];
 282             for (i = 0; i < len; ++i) {
 283                 newTable[i] = lineTable[i];
 284             }
 285             for (; i < newLen; ++i) {
 286                 newTable[i] = new LineTableRecord();
 287             }
 288             lineTable = newTable;
 289         }
 290     }
 291 
 292     void assureFileTableSize() {
 293         int len = fileTable == null? 0 : fileTable.length;
 294         if (fileIndex >= len) {
 295             int i;
 296             int newLen = len == 0? INIT_SIZE_FILE : len * 2;
 297             FileTableRecord[] newTable = new FileTableRecord[newLen];
 298             for (i = 0; i < len; ++i) {
 299                 newTable[i] = fileTable[i];
 300             }
 301             for (; i < newLen; ++i) {
 302                 newTable[i] = new FileTableRecord();
 303             }
 304             fileTable = newTable;
 305         }
 306     }
 307 
 308     void assureStratumTableSize() {
 309         int len = stratumTable == null? 0 : stratumTable.length;
 310         if (stratumIndex >= len) {
 311             int i;
 312             int newLen = len == 0? INIT_SIZE_STRATUM : len * 2;
 313             StratumTableRecord[] newTable = new StratumTableRecord[newLen];
 314             for (i = 0; i < len; ++i) {
 315                 newTable[i] = stratumTable[i];
 316             }
 317             for (; i < newLen; ++i) {
 318                 newTable[i] = new StratumTableRecord();
 319             }
 320             stratumTable = newTable;
 321         }
 322     }
 323 
 324     String readLine() {
 325         StringBuffer sb = new StringBuffer();
 326         char ch;
 327 
 328         ignoreWhite();
 329         while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
 330             sb.append(ch);
 331         }
 332         // check for CR LF
 333         if ((ch == '\r') && (sdePeek() == '\n')) {
 334             sdeRead();
 335         }
 336         ignoreWhite(); // leading white
 337         return sb.toString();
 338     }
 339 
 340     private int defaultStratumTableIndex() {
 341         if ((defaultStratumIndex == -1) && (defaultStratumId != null)) {
 342             defaultStratumIndex =
 343                 stratumTableIndex(defaultStratumId);
 344         }
 345         return defaultStratumIndex;
 346     }
 347 
 348     int stratumTableIndex(String stratumId) {
 349         int i;
 350 
 351         if (stratumId == null) {
 352             return defaultStratumTableIndex();
 353         }
 354         for (i = 0; i < (stratumIndex-1); ++i) {
 355             if (stratumTable[i].id.equals(stratumId)) {
 356                 return i;
 357             }
 358         }
 359         return defaultStratumTableIndex();
 360     }
 361 
 362     Stratum stratum(String stratumID) {
 363         int sti = stratumTableIndex(stratumID);
 364         return new Stratum(sti);
 365     }
 366 
 367     List<String> availableStrata() {
 368         List<String> strata = new ArrayList<String>();
 369 
 370         for (int i = 0; i < (stratumIndex-1); ++i) {
 371             StratumTableRecord rec = stratumTable[i];
 372             strata.add(rec.id);
 373         }
 374         return strata;
 375     }
 376 
 377 /*****************************
 378  * below functions/methods are written to compile under either Java or C
 379  *
 380  * Needed support functions:
 381  *   sdePeek()
 382  *   sdeRead()
 383  *   sdeAdvance()
 384  *   readLine()
 385  *   assureLineTableSize()
 386  *   assureFileTableSize()
 387  *   assureStratumTableSize()
 388  *   syntax()
 389  *
 390  *   stratumTableIndex(String)
 391  *
 392  * Needed support variables:
 393  *   lineTable
 394  *   lineIndex
 395  *   fileTable
 396  *   fileIndex
 397  *   currentFileId
 398  *
 399  * Needed types:
 400  *   String
 401  *
 402  * Needed constants:
 403  *   NullString
 404  */
 405 
 406     void ignoreWhite() {
 407         char ch;
 408 
 409         while (((ch = sdePeek()) == ' ') || (ch == '\t')) {
 410             sdeAdvance();
 411         }
 412     }
 413 
 414     void ignoreLine() {
 415         char ch;
 416 
 417         while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
 418         }
 419         /* check for CR LF */
 420         if ((ch == '\r') && (sdePeek() == '\n')) {
 421             sdeAdvance();
 422         }
 423         ignoreWhite(); /* leading white */
 424     }
 425 
 426     int readNumber() {
 427         int value = 0;
 428         char ch;
 429 
 430         ignoreWhite();
 431         while (((ch = sdePeek()) >= '0') && (ch <= '9')) {
 432             sdeAdvance();
 433             value = (value * 10) + ch - '0';
 434         }
 435         ignoreWhite();
 436         return value;
 437     }
 438 
 439     void storeFile(int fileId, String sourceName, String sourcePath) {
 440         assureFileTableSize();
 441         fileTable[fileIndex].fileId = fileId;
 442         fileTable[fileIndex].sourceName = sourceName;
 443         fileTable[fileIndex].sourcePath = sourcePath;
 444         ++fileIndex;
 445     }
 446 
 447     void fileLine() {
 448         int hasAbsolute = 0; /* acts as boolean */
 449         int fileId;
 450         String sourceName;
 451         String sourcePath = null;
 452 
 453         /* is there an absolute filename? */
 454         if (sdePeek() == '+') {
 455             sdeAdvance();
 456             hasAbsolute = 1;
 457         }
 458         fileId = readNumber();
 459         sourceName = readLine();
 460         if (hasAbsolute == 1) {
 461             sourcePath = readLine();
 462         }
 463 
 464         storeFile(fileId, sourceName, sourcePath);
 465     }
 466 
 467     void storeLine(int jplsStart, int jplsEnd, int jplsLineInc,
 468                   int njplsStart, int njplsEnd, int fileId) {
 469         assureLineTableSize();
 470         lineTable[lineIndex].jplsStart = jplsStart;
 471         lineTable[lineIndex].jplsEnd = jplsEnd;
 472         lineTable[lineIndex].jplsLineInc = jplsLineInc;
 473         lineTable[lineIndex].njplsStart = njplsStart;
 474         lineTable[lineIndex].njplsEnd = njplsEnd;
 475         lineTable[lineIndex].fileId = fileId;
 476         ++lineIndex;
 477     }
 478 
 479     /**
 480      * Parse line translation info.  Syntax is
 481      *     <NJ-start-line> [ # <file-id> ] [ , <line-count> ] :
 482      *                 <J-start-line> [ , <line-increment> ] CR
 483      */
 484     void lineLine() {
 485         int lineCount = 1;
 486         int lineIncrement = 1;
 487         int njplsStart;
 488         int jplsStart;
 489 
 490         njplsStart = readNumber();
 491 
 492         /* is there a fileID? */
 493         if (sdePeek() == '#') {
 494             sdeAdvance();
 495             currentFileId = readNumber();
 496         }
 497 
 498         /* is there a line count? */
 499         if (sdePeek() == ',') {
 500             sdeAdvance();
 501             lineCount = readNumber();
 502         }
 503 
 504         if (sdeRead() != ':') {
 505             syntax();
 506         }
 507         jplsStart = readNumber();
 508         if (sdePeek() == ',') {
 509             sdeAdvance();
 510             lineIncrement = readNumber();
 511         }
 512         ignoreLine(); /* flush the rest */
 513 
 514         storeLine(jplsStart,
 515                   jplsStart + (lineCount * lineIncrement) -1,
 516                   lineIncrement,
 517                   njplsStart,
 518                   njplsStart + lineCount -1,
 519                   currentFileId);
 520     }
 521 
 522     /**
 523      * Until the next stratum section, everything after this
 524      * is in stratumId - so, store the current indicies.
 525      */
 526     void storeStratum(String stratumId) {
 527         /* remove redundant strata */
 528         if (stratumIndex > 0) {
 529             if ((stratumTable[stratumIndex-1].fileIndex
 530                                             == fileIndex) &&
 531                 (stratumTable[stratumIndex-1].lineIndex
 532                                             == lineIndex)) {
 533                 /* nothing changed overwrite it */
 534                 --stratumIndex;
 535             }
 536         }
 537         /* store the results */
 538         assureStratumTableSize();
 539         stratumTable[stratumIndex].id = stratumId;
 540         stratumTable[stratumIndex].fileIndex = fileIndex;
 541         stratumTable[stratumIndex].lineIndex = lineIndex;
 542         ++stratumIndex;
 543         currentFileId = 0;
 544     }
 545 
 546     /**
 547      * The beginning of a stratum's info
 548      */
 549     void stratumSection() {
 550         storeStratum(readLine());
 551     }
 552 
 553     void fileSection() {
 554         ignoreLine();
 555         while (sdePeek() != '*') {
 556             fileLine();
 557         }
 558     }
 559 
 560     void lineSection() {
 561         ignoreLine();
 562         while (sdePeek() != '*') {
 563             lineLine();
 564         }
 565     }
 566 
 567     /**
 568      * Ignore a section we don't know about.
 569      */
 570     void ignoreSection() {
 571         ignoreLine();
 572         while (sdePeek() != '*') {
 573             ignoreLine();
 574         }
 575     }
 576 
 577     /**
 578      * A base "Java" stratum is always available, though
 579      * it is not in the SourceDebugExtension.
 580      * Create the base stratum.
 581      */
 582     void createJavaStratum() {
 583         baseStratumIndex = stratumIndex;
 584         storeStratum(BASE_STRATUM_NAME);
 585         storeFile(1, jplsFilename, NullString);
 586         /* JPL line numbers cannot exceed 65535 */
 587         storeLine(1, 65536, 1, 1, 65536, 1);
 588         storeStratum("Aux"); /* in case they don't declare */
 589     }
 590 
 591     /**
 592      * Decode a SourceDebugExtension which is in SourceMap format.
 593      * This is the entry point into the recursive descent parser.
 594      */
 595     void decode() {
 596         /* check for "SMAP" - allow EOF if not ours */
 597         if ((sourceDebugExtension.length() < 4) ||
 598             (sdeRead() != 'S') ||
 599             (sdeRead() != 'M') ||
 600             (sdeRead() != 'A') ||
 601             (sdeRead() != 'P')) {
 602             return; /* not our info */
 603         }
 604         ignoreLine(); /* flush the rest */
 605         jplsFilename = readLine();
 606         defaultStratumId = readLine();
 607         createJavaStratum();
 608         while (true) {
 609             if (sdeRead() != '*') {
 610                 syntax();
 611             }
 612             switch (sdeRead()) {
 613                 case 'S':
 614                     stratumSection();
 615                     break;
 616                 case 'F':
 617                     fileSection();
 618                     break;
 619                 case 'L':
 620                     lineSection();
 621                     break;
 622                 case 'E':
 623                     /* set end points */
 624                     storeStratum("*terminator*");
 625                     isValid = true;
 626                     return;
 627                 default:
 628                     ignoreSection();
 629             }
 630         }
 631     }
 632 
 633     void createProxyForAbsentSDE() {
 634         jplsFilename = null;
 635         defaultStratumId = BASE_STRATUM_NAME;
 636         defaultStratumIndex = stratumIndex;
 637         createJavaStratum();
 638         storeStratum("*terminator*");
 639     }
 640 
 641     /***************** query functions ***********************/
 642 
 643     private int stiLineTableIndex(int sti, int jplsLine) {
 644         int i;
 645         int lineIndexStart;
 646         int lineIndexEnd;
 647 
 648         lineIndexStart = stratumTable[sti].lineIndex;
 649         /* one past end */
 650         lineIndexEnd = stratumTable[sti+1].lineIndex;
 651         for (i = lineIndexStart; i < lineIndexEnd; ++i) {
 652             if ((jplsLine >= lineTable[i].jplsStart) &&
 653                             (jplsLine <= lineTable[i].jplsEnd)) {
 654                 return i;
 655             }
 656         }
 657         return -1;
 658     }
 659 
 660     private int stiLineNumber(int sti, int lti, int jplsLine) {
 661         return lineTable[lti].njplsStart +
 662                 (((jplsLine - lineTable[lti].jplsStart) /
 663                                    lineTable[lti].jplsLineInc));
 664     }
 665 
 666     private int fileTableIndex(int sti, int fileId) {
 667         int i;
 668         int fileIndexStart = stratumTable[sti].fileIndex;
 669         /* one past end */
 670         int fileIndexEnd = stratumTable[sti+1].fileIndex;
 671         for (i = fileIndexStart; i < fileIndexEnd; ++i) {
 672             if (fileTable[i].fileId == fileId) {
 673                 return i;
 674             }
 675         }
 676         return -1;
 677     }
 678 
 679     private int stiFileTableIndex(int sti, int lti) {
 680         return fileTableIndex(sti, lineTable[lti].fileId);
 681     }
 682 
 683     boolean isValid() {
 684         return isValid;
 685     }
 686 }