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); } } }