/*
* Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdi;
import com.sun.jdi.*;
import java.util.*;
import java.io.File;
class SDE {
private static final int INIT_SIZE_FILE = 3;
private static final int INIT_SIZE_LINE = 100;
private static final int INIT_SIZE_STRATUM = 3;
static final String BASE_STRATUM_NAME = "Java";
/* for C capatibility */
static final String NullString = null;
private class FileTableRecord {
int fileId;
String sourceName;
String sourcePath; // do not read - use accessor
boolean isConverted = false;
/**
* Return the sourcePath, computing it if not set.
* If set, convert '/' in the sourcePath to the
* local file separator.
*/
String getSourcePath(ReferenceTypeImpl refType) {
if (!isConverted) {
if (sourcePath == null) {
sourcePath = refType.baseSourceDir() + sourceName;
} else {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < sourcePath.length(); ++i) {
char ch = sourcePath.charAt(i);
if (ch == '/') {
buf.append(File.separatorChar);
} else {
buf.append(ch);
}
}
sourcePath = buf.toString();
}
isConverted = true;
}
return sourcePath;
}
}
private class LineTableRecord {
int jplsStart;
int jplsEnd;
int jplsLineInc;
int njplsStart;
int njplsEnd;
int fileId;
}
private class StratumTableRecord {
String id;
int fileIndex;
int lineIndex;
}
class Stratum {
private final int sti; /* stratum index */
private Stratum(int sti) {
this.sti = sti;
}
String id() {
return stratumTable[sti].id;
}
boolean isJava() {
return sti == baseStratumIndex;
}
/**
* Return all the sourceNames for this stratum.
* Look from our starting fileIndex upto the starting
* fileIndex of next stratum - can do this since there
* is always a terminator stratum.
* Default sourceName (the first one) must be first.
*/
List<String> sourceNames(ReferenceTypeImpl refType) {
int i;
int fileIndexStart = stratumTable[sti].fileIndex;
/* one past end */
int fileIndexEnd = stratumTable[sti+1].fileIndex;
List<String> result = new ArrayList<String>(fileIndexEnd - fileIndexStart);
for (i = fileIndexStart; i < fileIndexEnd; ++i) {
result.add(fileTable[i].sourceName);
}
return result;
}
/**
* Return all the sourcePaths for this stratum.
* Look from our starting fileIndex upto the starting
* fileIndex of next stratum - can do this since there
* is always a terminator stratum.
* Default sourcePath (the first one) must be first.
*/
List<String> sourcePaths(ReferenceTypeImpl refType) {
int i;
int fileIndexStart = stratumTable[sti].fileIndex;
/* one past end */
int fileIndexEnd = stratumTable[sti+1].fileIndex;
List<String> result = new ArrayList<String>(fileIndexEnd - fileIndexStart);
for (i = fileIndexStart; i < fileIndexEnd; ++i) {
result.add(fileTable[i].getSourcePath(refType));
}
return result;
}
LineStratum lineStratum(ReferenceTypeImpl refType,
int jplsLine) {
int lti = stiLineTableIndex(sti, jplsLine);
if (lti < 0) {
return null;
} else {
return new LineStratum(sti, lti, refType,
jplsLine);
}
}
}
class LineStratum {
private final int sti; /* stratum index */
private final int lti; /* line table index */
private final ReferenceTypeImpl refType;
private final int jplsLine;
private String sourceName = null;
private String sourcePath = null;
private LineStratum(int sti, int lti,
ReferenceTypeImpl refType,
int jplsLine) {
this.sti = sti;
this.lti = lti;
this.refType = refType;
this.jplsLine = jplsLine;
}
public boolean equals(Object obj) {
if ((obj != null) && (obj instanceof LineStratum)) {
LineStratum other = (LineStratum)obj;
return (lti == other.lti) &&
(sti == other.sti) &&
(lineNumber() == other.lineNumber()) &&
(refType.equals(other.refType));
} else {
return false;
}
}
int lineNumber() {
return stiLineNumber(sti, lti, jplsLine);
}
/**
* Fetch the source name and source path for
* this line, converting or constructing
* the source path if needed.
*/
void getSourceInfo() {
if (sourceName != null) {
// already done
return;
}
int fti = stiFileTableIndex(sti, lti);
if (fti == -1) {
throw new InternalError(
"Bad SourceDebugExtension, no matching source id " +
lineTable[lti].fileId + " jplsLine: " + jplsLine);
}
FileTableRecord ftr = fileTable[fti];
sourceName = ftr.sourceName;
sourcePath = ftr.getSourcePath(refType);
}
String sourceName() {
getSourceInfo();
return sourceName;
}
String sourcePath() {
getSourceInfo();
return sourcePath;
}
}
private FileTableRecord[] fileTable = null;
private LineTableRecord[] lineTable = null;
private StratumTableRecord[] stratumTable = null;
private int fileIndex = 0;
private int lineIndex = 0;
private int stratumIndex = 0;
private int currentFileId = 0;
private int defaultStratumIndex = -1;
private int baseStratumIndex = -2; /* so as not to match -1 above */
private int sdePos = 0;
final String sourceDebugExtension;
String jplsFilename = null;
String defaultStratumId = null;
boolean isValid = false;
SDE(String sourceDebugExtension) {
this.sourceDebugExtension = sourceDebugExtension;
decode();
}
SDE() {
this.sourceDebugExtension = null;
createProxyForAbsentSDE();
}
char sdePeek() {
if (sdePos >= sourceDebugExtension.length()) {
syntax();
}
return sourceDebugExtension.charAt(sdePos);
}
char sdeRead() {
if (sdePos >= sourceDebugExtension.length()) {
syntax();
}
return sourceDebugExtension.charAt(sdePos++);
}
void sdeAdvance() {
sdePos++;
}
void syntax() {
throw new InternalError("bad SourceDebugExtension syntax - position " +
sdePos);
}
void syntax(String msg) {
throw new InternalError("bad SourceDebugExtension syntax: " + msg);
}
void assureLineTableSize() {
int len = lineTable == null? 0 : lineTable.length;
if (lineIndex >= len) {
int i;
int newLen = len == 0? INIT_SIZE_LINE : len * 2;
LineTableRecord[] newTable = new LineTableRecord[newLen];
for (i = 0; i < len; ++i) {
newTable[i] = lineTable[i];
}
for (; i < newLen; ++i) {
newTable[i] = new LineTableRecord();
}
lineTable = newTable;
}
}
void assureFileTableSize() {
int len = fileTable == null? 0 : fileTable.length;
if (fileIndex >= len) {
int i;
int newLen = len == 0? INIT_SIZE_FILE : len * 2;
FileTableRecord[] newTable = new FileTableRecord[newLen];
for (i = 0; i < len; ++i) {
newTable[i] = fileTable[i];
}
for (; i < newLen; ++i) {
newTable[i] = new FileTableRecord();
}
fileTable = newTable;
}
}
void assureStratumTableSize() {
int len = stratumTable == null? 0 : stratumTable.length;
if (stratumIndex >= len) {
int i;
int newLen = len == 0? INIT_SIZE_STRATUM : len * 2;
StratumTableRecord[] newTable = new StratumTableRecord[newLen];
for (i = 0; i < len; ++i) {
newTable[i] = stratumTable[i];
}
for (; i < newLen; ++i) {
newTable[i] = new StratumTableRecord();
}
stratumTable = newTable;
}
}
String readLine() {
StringBuffer sb = new StringBuffer();
char ch;
ignoreWhite();
while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
sb.append(ch);
}
// check for CR LF
if ((ch == '\r') && (sdePeek() == '\n')) {
sdeRead();
}
ignoreWhite(); // leading white
return sb.toString();
}
private int defaultStratumTableIndex() {
if ((defaultStratumIndex == -1) && (defaultStratumId != null)) {
defaultStratumIndex =
stratumTableIndex(defaultStratumId);
}
return defaultStratumIndex;
}
int stratumTableIndex(String stratumId) {
int i;
if (stratumId == null) {
return defaultStratumTableIndex();
}
for (i = 0; i < (stratumIndex-1); ++i) {
if (stratumTable[i].id.equals(stratumId)) {
return i;
}
}
return defaultStratumTableIndex();
}
Stratum stratum(String stratumID) {
int sti = stratumTableIndex(stratumID);
return new Stratum(sti);
}
List<String> availableStrata() {
List<String> strata = new ArrayList<String>();
for (int i = 0; i < (stratumIndex-1); ++i) {
StratumTableRecord rec = stratumTable[i];
strata.add(rec.id);
}
return strata;
}
/*****************************
* below functions/methods are written to compile under either Java or C
*
* Needed support functions:
* sdePeek()
* sdeRead()
* sdeAdvance()
* readLine()
* assureLineTableSize()
* assureFileTableSize()
* assureStratumTableSize()
* syntax()
*
* stratumTableIndex(String)
*
* Needed support variables:
* lineTable
* lineIndex
* fileTable
* fileIndex
* currentFileId
*
* Needed types:
* String
*
* Needed constants:
* NullString
*/
void ignoreWhite() {
char ch;
while (((ch = sdePeek()) == ' ') || (ch == '\t')) {
sdeAdvance();
}
}
void ignoreLine() {
char ch;
while (((ch = sdeRead()) != '\n') && (ch != '\r')) {
}
/* check for CR LF */
if ((ch == '\r') && (sdePeek() == '\n')) {
sdeAdvance();
}
ignoreWhite(); /* leading white */
}
int readNumber() {
int value = 0;
char ch;
ignoreWhite();
while (((ch = sdePeek()) >= '0') && (ch <= '9')) {
sdeAdvance();
value = (value * 10) + ch - '0';
}
ignoreWhite();
return value;
}
void storeFile(int fileId, String sourceName, String sourcePath) {
assureFileTableSize();
fileTable[fileIndex].fileId = fileId;
fileTable[fileIndex].sourceName = sourceName;
fileTable[fileIndex].sourcePath = sourcePath;
++fileIndex;
}
void fileLine() {
int hasAbsolute = 0; /* acts as boolean */
int fileId;
String sourceName;
String sourcePath = null;
/* is there an absolute filename? */
if (sdePeek() == '+') {
sdeAdvance();
hasAbsolute = 1;
}
fileId = readNumber();
sourceName = readLine();
if (hasAbsolute == 1) {
sourcePath = readLine();
}
storeFile(fileId, sourceName, sourcePath);
}
void storeLine(int jplsStart, int jplsEnd, int jplsLineInc,
int njplsStart, int njplsEnd, int fileId) {
assureLineTableSize();
lineTable[lineIndex].jplsStart = jplsStart;
lineTable[lineIndex].jplsEnd = jplsEnd;
lineTable[lineIndex].jplsLineInc = jplsLineInc;
lineTable[lineIndex].njplsStart = njplsStart;
lineTable[lineIndex].njplsEnd = njplsEnd;
lineTable[lineIndex].fileId = fileId;
++lineIndex;
}
/**
* Parse line translation info. Syntax is
* <NJ-start-line> [ # <file-id> ] [ , <line-count> ] :
* <J-start-line> [ , <line-increment> ] CR
*/
void lineLine() {
int lineCount = 1;
int lineIncrement = 1;
int njplsStart;
int jplsStart;
njplsStart = readNumber();
/* is there a fileID? */
if (sdePeek() == '#') {
sdeAdvance();
currentFileId = readNumber();
}
/* is there a line count? */
if (sdePeek() == ',') {
sdeAdvance();
lineCount = readNumber();
}
if (sdeRead() != ':') {
syntax();
}
jplsStart = readNumber();
if (sdePeek() == ',') {
sdeAdvance();
lineIncrement = readNumber();
}
ignoreLine(); /* flush the rest */
storeLine(jplsStart,
jplsStart + (lineCount * lineIncrement) -1,
lineIncrement,
njplsStart,
njplsStart + lineCount -1,
currentFileId);
}
/**
* Until the next stratum section, everything after this
* is in stratumId - so, store the current indicies.
*/
void storeStratum(String stratumId) {
/* remove redundant strata */
if (stratumIndex > 0) {
if ((stratumTable[stratumIndex-1].fileIndex
== fileIndex) &&
(stratumTable[stratumIndex-1].lineIndex
== lineIndex)) {
/* nothing changed overwrite it */
--stratumIndex;
}
}
/* store the results */
assureStratumTableSize();
stratumTable[stratumIndex].id = stratumId;
stratumTable[stratumIndex].fileIndex = fileIndex;
stratumTable[stratumIndex].lineIndex = lineIndex;
++stratumIndex;
currentFileId = 0;
}
/**
* The beginning of a stratum's info
*/
void stratumSection() {
storeStratum(readLine());
}
void fileSection() {
ignoreLine();
while (sdePeek() != '*') {
fileLine();
}
}
void lineSection() {
ignoreLine();
while (sdePeek() != '*') {
lineLine();
}
}
/**
* Ignore a section we don't know about.
*/
void ignoreSection() {
ignoreLine();
while (sdePeek() != '*') {
ignoreLine();
}
}
/**
* A base "Java" stratum is always available, though
* it is not in the SourceDebugExtension.
* Create the base stratum.
*/
void createJavaStratum() {
baseStratumIndex = stratumIndex;
storeStratum(BASE_STRATUM_NAME);
storeFile(1, jplsFilename, NullString);
/* JPL line numbers cannot exceed 65535 */
storeLine(1, 65536, 1, 1, 65536, 1);
storeStratum("Aux"); /* in case they don't declare */
}
/**
* Decode a SourceDebugExtension which is in SourceMap format.
* This is the entry point into the recursive descent parser.
*/
void decode() {
/* check for "SMAP" - allow EOF if not ours */
if ((sourceDebugExtension.length() < 4) ||
(sdeRead() != 'S') ||
(sdeRead() != 'M') ||
(sdeRead() != 'A') ||
(sdeRead() != 'P')) {
return; /* not our info */
}
ignoreLine(); /* flush the rest */
jplsFilename = readLine();
defaultStratumId = readLine();
createJavaStratum();
while (true) {
if (sdeRead() != '*') {
syntax();
}
switch (sdeRead()) {
case 'S':
stratumSection();
break;
case 'F':
fileSection();
break;
case 'L':
lineSection();
break;
case 'E':
/* set end points */
storeStratum("*terminator*");
isValid = true;
return;
default:
ignoreSection();
}
}
}
void createProxyForAbsentSDE() {
jplsFilename = null;
defaultStratumId = BASE_STRATUM_NAME;
defaultStratumIndex = stratumIndex;
createJavaStratum();
storeStratum("*terminator*");
}
/***************** query functions ***********************/
private int stiLineTableIndex(int sti, int jplsLine) {
int i;
int lineIndexStart;
int lineIndexEnd;
lineIndexStart = stratumTable[sti].lineIndex;
/* one past end */
lineIndexEnd = stratumTable[sti+1].lineIndex;
for (i = lineIndexStart; i < lineIndexEnd; ++i) {
if ((jplsLine >= lineTable[i].jplsStart) &&
(jplsLine <= lineTable[i].jplsEnd)) {
return i;
}
}
return -1;
}
private int stiLineNumber(int sti, int lti, int jplsLine) {
return lineTable[lti].njplsStart +
(((jplsLine - lineTable[lti].jplsStart) /
lineTable[lti].jplsLineInc));
}
private int fileTableIndex(int sti, int fileId) {
int i;
int fileIndexStart = stratumTable[sti].fileIndex;
/* one past end */
int fileIndexEnd = stratumTable[sti+1].fileIndex;
for (i = fileIndexStart; i < fileIndexEnd; ++i) {
if (fileTable[i].fileId == fileId) {
return i;
}
}
return -1;
}
private int stiFileTableIndex(int sti, int lti) {
return fileTableIndex(sti, lineTable[lti].fileId);
}
boolean isValid() {
return isValid;
}
}