package xtc.lang.blink;
import java.io.FileReader;
import java.io.IOException;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A symbol remapper class. This is to handle symbol remapping in the Jeannie
* source code. For instance, "y" in Jeannie source code could be access by
* "pcEnv->y" in the gdb. Therefore, we need a symbol remapping for the Jeannie
* source code.
*
* @author Byeongcheol Lee
*/
public class SymbolMapper {
/** The target source language. */
enum TargetSourceLanguage {C, JAVA};
/**
* The Jeannie source level variable remapping. The blink debugger search the
* source file direction for the variable remapping table. For instance, the
* blink debugger expect to find a remap file, "Main.jni.symbol" for the
* source file "Main.jni."
*/
private final HashMap<String, SymbolMapper.SourceVariableMapper> varibleRemap
= new HashMap<String, SourceVariableMapper>();
/**
* @param dbg The debugger.
*/
SymbolMapper() {}
/**
* Tries to find the Jeannie variable remap information.
*
* @param variable The variable name.
* @param sourceFile The source file.
* @param sourceLineNumber The source file line number.
* @return a variable remapper object if sucessful otherwise null.
*/
public VariableRemapEntry lookUpVariableRemap(String variable,
String sourceFile, int sourceLineNumber) {
if (sourceFile == null || sourceLineNumber < 0)
return null;
SourceVariableMapper mapper = getRemapInformation(sourceFile);
if (mapper == null) {
return null;
}
return mapper.lookUp(variable, sourceLineNumber);
}
/**
* Look up a method name remapping.
*
* @param methodOrFunction The name in the target language.
* @param sourceFile The source file.
* @return The remapping.
*/
public MethodRemapEntry lookupMethodRemap(String methodOrFunction,
String sourceFile) {
if (sourceFile == null) {
return null;
}
SourceVariableMapper mapper = getRemapInformation(sourceFile);
if (mapper == null) {
return null;
}
return mapper.lookupMethod(methodOrFunction);
}
/**
* Look up a source variable remapping.
*
* @param sourceFileName The source file name.
* @return The remapping.
*/
private SourceVariableMapper getRemapInformation(String sourceFileName) {
SourceVariableMapper mapper = null;
if (!varibleRemap.containsKey(sourceFileName)) {
//the first time
try {
String remapFile = sourceFileName + ".symbols";
mapper = new SourceVariableMapper(remapFile);
} catch (IOException e) {
}
} else {
mapper = varibleRemap.get(sourceFileName);
}
return mapper;
}
/**
* Look up variable remapping.
*
* @param sourceFile The source file.
* @param line The source line.
* @return The remap entries.
*/
public List<VariableRemapEntry> lookup(String sourceFile, int line) {
LinkedList<VariableRemapEntry> list = new LinkedList<VariableRemapEntry>();
SourceVariableMapper mapper = getRemapInformation(sourceFile);
if (mapper == null) {
return list;
}
for(VariableRemapEntry e : mapper.ventries) {
if (e.startLine <= line && line <= e.endLine) {
list.add(e);
}
}
return list;
}
/**
* A source-level view of the current position. This is a
* <source file, source line> pair.
*/
static class SourceFileAndLine {
/** The source File. */
private final String sourceFile;
/** The source line. */
private final int sourceLine;
/**
* The constructor.
*
* @param sourceFile The source file.
* @param sourceLine The source line.
*/
SourceFileAndLine(String sourceFile, int sourceLine) {
this.sourceFile = sourceFile;
this.sourceLine = sourceLine;
}
/** Getter method for the source file. */
public String getSourceFile() {
return sourceFile;
}
/** Getter method for the source line. */
public int getSourceLine() {
return sourceLine;
}
/** Get source file and line format. */
public String toString() {
return sourceFile + ":" + sourceLine;
}
}
/**
* A source level variable entry.
*/
static class VariableRemapEntry {
/** The start line. */
final int startLine;
/** The start column. */
final int startColumn;
/** The end line. */
final int endLine;
/** The end column. */
final int endColumn;
/** The language. */
final TargetSourceLanguage targetLanguage;
/** The source variable name. */
final String sourceVariableName;
/** The target variable name. */
final String targetVariableName;
/**
* Constructor.
*
* @param startLine The start line in the target source file.
* @param startColumn The start column in the target source file.
* @param endLine The end line in the target source file.
* @param endColumn The end column in the target source file.
* @param targetLanguage The target language (C or Java).
* @param sourceVariableName The variable name in the source language.
* @param targetVariableName The variable name in the target language.
*/
VariableRemapEntry(int startLine, int startColumn, int endLine,
int endColumn, TargetSourceLanguage targetLanguage,
String sourceVariableName, String targetVariableName) {
this.startLine = startLine;
this.startColumn = startColumn;
this.endLine = endLine;
this.endColumn = endColumn;
this.targetLanguage = targetLanguage;
this.sourceVariableName = sourceVariableName;
this.targetVariableName = targetVariableName;
}
/**
* Generate the variable expression in the target language environment such
* that jdb or gdb can recognize the expression.
*
* @return jdb or gdb expression for the variable.
*/
String targetLanguageExpression() {
StringBuffer sb = new StringBuffer();
switch(targetLanguage) {
case JAVA:
sb.append("this.").append(targetVariableName);
break;
case C:
sb.append("pcEnv->").append(targetVariableName);
break;
default:
assert false;
break;
}
return sb.toString();
}
}
/**
* A method remap entry.
*
*/
static class MethodRemapEntry {
/** The symbol name in the source language. */
final String sourceLanguageName;
/** The symbol name in the target language. */
final String targetLanguageName;
/**
* The constructor.
*
* @param sourceLanguageName The name in the source langguage.
* @param targetLanguageName The name in the target langauge.
*/
public MethodRemapEntry(final String sourceLanguageName,
final String targetLanguageName) {
this.sourceLanguageName = sourceLanguageName;
this.targetLanguageName = targetLanguageName;
}
/** Getter method for source langauge. */
public String getSourceLanguageName() {
return sourceLanguageName;
}
/** Getter method for target langauge. */
public String getTargetLanguageName() {
return targetLanguageName;
}
}
/**
* A source level variable remapping table for a source file.
*/
private static class SourceVariableMapper {
/** The remap file. */
private final String remapFile;
/** The list of variable remap entry. */
private final List<VariableRemapEntry> ventries = new LinkedList<VariableRemapEntry>();
/** The list of method name remap entry. */
private final HashMap<String, MethodRemapEntry> mentries = new HashMap<String, MethodRemapEntry>();
/**
* The constructor.
*
* @param remapFile The file containing the variable remapping information.
*/
public SourceVariableMapper(String remapFile) throws IOException {
this.remapFile = remapFile;
updateRemapEntry();
}
/** Update the remap entry from the remap file.*/
private void updateRemapEntry() throws IOException {
ventries.clear();
Pattern variableRemapEntryPattern = Pattern
.compile("\\s*(\\S+) (\\d+) (\\d+) (\\d+) (\\d+) (Java|C) (\\S+) (\\S+)");
Pattern methodRemapEntryPattern = Pattern.compile("\\s*(\\S+)\\s*(\\S+)");
BufferedReader br = new BufferedReader(new FileReader(remapFile));
int phase = 0;
for (String line = br.readLine(); line != null; line = br.readLine()) {
if (line.equals("LocalVariableMap:")) {
phase = 1;
continue;
}
if (line.equals("MethodMap:")) {
phase = 2;
continue;
}
if (phase == 1) {
Matcher m = variableRemapEntryPattern.matcher(line);
if (m.matches()) {
int startLine = Integer.parseInt(m.group(2));
int startColumn = Integer.parseInt(m.group(3));
int endLine = Integer.parseInt(m.group(4));
int endColumn = Integer.parseInt(m.group(5));
String targetLanguageStr = m.group(6);
TargetSourceLanguage tlang;
if (targetLanguageStr.equals("Java")) {
tlang = TargetSourceLanguage.JAVA;
} else {
assert targetLanguageStr.equals("C");
tlang = TargetSourceLanguage.C;
}
String sourceVariableName = m.group(7);
String targetVariableName = m.group(8);
VariableRemapEntry e = new VariableRemapEntry(startLine,
startColumn, endLine, endColumn, tlang, sourceVariableName,
targetVariableName);
ventries.add(e);
}
} else if (phase == 2) {
Matcher mMethodRemap = methodRemapEntryPattern.matcher(line);
if (mMethodRemap.matches()) {
String nameInSourceLanguage = mMethodRemap.group(1);
String nameInTargetLanguage = mMethodRemap.group(2);
MethodRemapEntry e = new MethodRemapEntry(nameInSourceLanguage,
nameInTargetLanguage);
mentries.put(nameInTargetLanguage, e);
}
}
}
br.close();
}
/**
* Look up for the variable remap entry.
*
* @param var The variable name.
* @param sourceLine The source line number for the variable.
* @return A VariableRemapEntry object if found, or null otherwise.
*/
VariableRemapEntry lookUp(String var, int sourceLine) {
for (final VariableRemapEntry e : ventries) {
if (sourceLine >= e.startLine && sourceLine <= e.endLine
&& e.sourceVariableName.equals(var)) {
return e;
}
}
return null;
}
/**
* Look up by method name.
*
* @param method The method name.
* @return The methe name remap entry.
*/
MethodRemapEntry lookupMethod(String method) {
return mentries.get(method);
}
}
}