/* * 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 instanceof LineStratum) { LineStratum other = (LineStratum)obj; return (lti == other.lti) && (sti == other.sti) && (lineNumber() == other.lineNumber()) && (refType.equals(other.refType)); } else { return false; } } @Override public int hashCode() { return (lineNumber() * 17) ^ refType.hashCode(); } 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; } }