GP-233 Added sleigh source file names to constructor info

This commit is contained in:
ghidra1 2021-01-26 10:53:18 -05:00
parent 9dd458e9e1
commit ec5009f613
14 changed files with 415 additions and 76 deletions

View file

@ -26,6 +26,7 @@ import ghidra.app.plugin.processors.sleigh.template.ConstructTpl;
import ghidra.app.plugin.processors.sleigh.template.HandleTpl;
import ghidra.program.model.lang.UnknownInstructionException;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.util.Msg;
import ghidra.util.xml.SpecXmlUtils;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
@ -55,6 +56,7 @@ public class Constructor implements Comparable<Constructor> {
in original (uncompiled) specfile */
private int flowthruindex;
private String sourceFile;
public Constructor() {
parent = null;
@ -115,14 +117,14 @@ public class Constructor implements Comparable<Constructor> {
public String print(ParserWalker walker) throws MemoryAccessException {
String res = "";
for (int i = 0; i < printpiece.length; ++i) {
if (printpiece[i].length() != 0) {
if (printpiece[i].charAt(0) == '\n') {
int index = printpiece[i].charAt(1) - 'A';
for (String element : printpiece) {
if (element.length() != 0) {
if (element.charAt(0) == '\n') {
int index = element.charAt(1) - 'A';
res += operands[index].print(walker);
}
else {
res += printpiece[i];
res += element;
}
}
}
@ -130,7 +132,7 @@ public class Constructor implements Comparable<Constructor> {
}
public String printSeparator(int separatorIndex) {
// Separator is all chars to the left of the corresponding operand
// The mnemonic (first sequence of contiguous non-space print-pieces)
// is ignored when identifying the first separator (index 0) and the
@ -140,7 +142,7 @@ public class Constructor implements Comparable<Constructor> {
if (separatorIndex < 0 || separatorIndex > operands.length) {
return null;
}
String cachedSeparator = separators[separatorIndex];
if (cachedSeparator != null) {
if (cachedSeparator.length() == 0) {
@ -188,11 +190,11 @@ public class Constructor implements Comparable<Constructor> {
FixedHandle lastHandle = null;
int lastHandleIndex = -1;
for (int i = 0; i < printpiece.length; ++i) {
for (String element : printpiece) {
int prevSize = list.size();
if (printpiece[i].length() != 0) {
if (printpiece[i].charAt(0) == '\n') {
int index = printpiece[i].charAt(1) - 'A';
if (element.length() != 0) {
if (element.charAt(0) == '\n') {
int index = element.charAt(1) - 'A';
operands[index].printList(walker, list);
if (prevSize != list.size() && ++opSymbolCnt == 1) {
// Identify sole handle which can be fixed
@ -212,8 +214,9 @@ public class Constructor implements Comparable<Constructor> {
}
}
else {
for (int j = 0; j < printpiece[i].length(); ++j)
list.add(new Character(printpiece[i].charAt(j)));
for (int j = 0; j < element.length(); ++j) {
list.add(new Character(element.charAt(j)));
}
}
}
}
@ -264,8 +267,9 @@ public class Constructor implements Comparable<Constructor> {
return res;
}
}
if (firstwhitespace == -1)
if (firstwhitespace == -1) {
return res; // Nothing to print
}
for (int i = firstwhitespace + 1; i < printpiece.length; ++i) {
if (printpiece[i].length() != 0) {
if (printpiece[i].charAt(0) == '\n') {
@ -289,8 +293,8 @@ public class Constructor implements Comparable<Constructor> {
*/
public void applyContext(ParserWalker walker, SleighDebugLogger debug)
throws MemoryAccessException {
for (int i = 0; i < context.length; ++i) {
context[i].apply(walker, debug);
for (ContextChange element : context) {
element.apply(walker, debug);
}
}
@ -300,10 +304,12 @@ public class Constructor implements Comparable<Constructor> {
* @return the named section (or null)
*/
public ConstructTpl getNamedTempl(int secnum) {
if (namedtempl == null)
if (namedtempl == null) {
return null;
if (secnum < namedtempl.size())
}
if (secnum < namedtempl.size()) {
return namedtempl.get(secnum);
}
return null;
}
@ -316,7 +322,17 @@ public class Constructor implements Comparable<Constructor> {
parent = (SubtableSymbol) symtab.findSymbol(myId);
firstwhitespace = SpecXmlUtils.decodeInt(el.getAttribute("first"));
minimumlength = SpecXmlUtils.decodeInt(el.getAttribute("length"));
lineno = SpecXmlUtils.decodeInt(el.getAttribute("line"));
String sourceAndLine = el.getAttribute("line");
String[] parts = sourceAndLine.split(":");
if (parts.length != 2) {
Msg.error(this, "Bad line attribute in .sla file");
lineno = -1;
sourceFile = "UNKNOWN";
}
else {
lineno = Integer.parseInt(parts[1].trim());
sourceFile = sleigh.getSourceFileIndexer().getFileName(Integer.parseInt(parts[0].trim()));
}
ArrayList<Object> oplist = new ArrayList<>();
ArrayList<Object> piecelist = new ArrayList<>();
@ -354,17 +370,21 @@ public class Constructor implements Comparable<Constructor> {
ConstructTpl curtempl = new ConstructTpl();
int sectionid = curtempl.restoreXml(parser, sleigh.getAddressFactory());
if (sectionid < 0) {
if (templ != null)
if (templ != null) {
throw new UnknownInstructionException("Duplicate main template section");
}
templ = curtempl;
}
else {
if (namedtempl == null)
if (namedtempl == null) {
namedtempl = new ArrayList<>();
while (namedtempl.size() <= sectionid)
}
while (namedtempl.size() <= sectionid) {
namedtempl.add(null);
if (namedtempl.get(sectionid) != null)
}
if (namedtempl.get(sectionid) != null) {
throw new UnknownInstructionException("Duplicate named template section");
}
namedtempl.set(sectionid, curtempl);
}
}
@ -378,10 +398,12 @@ public class Constructor implements Comparable<Constructor> {
context = new ContextChange[coplist.size()];
coplist.toArray(context);
if ((printpiece.length == 1) && (printpiece[0].length() >= 2) &&
(printpiece[0].charAt(0) == '\n'))
(printpiece[0].charAt(0) == '\n')) {
flowthruindex = printpiece[0].charAt(1) - 'A';
else
}
else {
flowthruindex = -1;
}
parser.end(el);
}
@ -391,18 +413,21 @@ public class Constructor implements Comparable<Constructor> {
* @return array of operand indices
*/
public int[] getOpsPrintOrder() {
if (firstwhitespace == -1)
if (firstwhitespace == -1) {
return new int[0];
}
int count = 0;
for (int i = firstwhitespace + 1; i < printpiece.length; ++i) {
if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n')
if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') {
count += 1;
}
}
int[] res = new int[count];
count = 0;
for (int i = firstwhitespace + 1; i < printpiece.length; ++i) {
if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n')
if (printpiece[i].length() != 0 && printpiece[i].charAt(0) == '\n') {
res[count++] = printpiece[i].charAt(1) - 'A';
}
}
return res;
}
@ -444,4 +469,12 @@ public class Constructor implements Comparable<Constructor> {
}
return true;
}
/**
* Returns the source file
* @return source file
*/
public String getSourceFile() {
return sourceFile;
}
}

View file

@ -84,8 +84,8 @@ public class SleighDebugLogger {
}
if (!(language instanceof SleighLanguage)) {
throw new IllegalArgumentException("unsupport language provider: " +
language.getClass().getSimpleName());
throw new IllegalArgumentException(
"unsupport language provider: " + language.getClass().getSimpleName());
}
ContextCache contextCache = new ContextCache();
@ -112,9 +112,8 @@ public class SleighDebugLogger {
}
try {
prototype =
new SleighInstructionPrototype((SleighLanguage) language, buf, context,
contextCache, false, this);
prototype = new SleighInstructionPrototype((SleighLanguage) language, buf, context,
contextCache, false, this);
prototype.cacheInfo(buf, context, false);
@ -148,8 +147,9 @@ public class SleighDebugLogger {
* @throws IllegalArgumentException if program language provider is not Sleigh
*/
public SleighDebugLogger(Program program, Address start, SleighDebugMode mode) {
this(new MemoryBufferImpl(program.getMemory(), start), new MyProcessorContextView(
program.getProgramContext(), start), program.getLanguage(), mode);
this(new MemoryBufferImpl(program.getMemory(), start),
new MyProcessorContextView(program.getProgramContext(), start), program.getLanguage(),
mode);
}
private class DebugInstructionContext implements InstructionContext {
@ -240,7 +240,8 @@ public class SleighDebugLogger {
if (!"instruction".equals(tableName) || name.startsWith("\n")) {
name = tableName;
}
list.add(name + "(" + Integer.toString(ct.getLineno()) + ")");
list.add(name + "(" + ct.getSourceFile() + ":" + Integer.toString(ct.getLineno()) + ")");
int flowthruindex = ct.getFlowthruIndex();
if (flowthruindex != -1) {
@ -338,8 +339,7 @@ public class SleighDebugLogger {
endbit = endbit % 8;
for (int i = 0; i < value.length; i++) {
String byteStr =
StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8);
String byteStr = StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8);
if (startbit >= 0) {
if (endByte == i) {
byteStr =
@ -621,8 +621,8 @@ public class SleighDebugLogger {
Math.min(contextBaseRegister.getMinimumByteSize() - byteOffset, maskvec.length * 4);
byte[] maskPatternValue = new byte[2 * contextBaseRegister.getMinimumByteSize()];
System.arraycopy(getBytes(valvec), 0, maskPatternValue, (maskPatternValue.length / 2) +
byteOffset, vecByteCnt);
System.arraycopy(getBytes(valvec), 0, maskPatternValue,
(maskPatternValue.length / 2) + byteOffset, vecByteCnt);
byte[] mask = getBytes(maskvec);
System.arraycopy(mask, 0, maskActualValue, byteOffset, vecByteCnt);
@ -640,9 +640,8 @@ public class SleighDebugLogger {
BigInteger actual = childActualValue.getUnsignedValueIgnoreMask();
BigInteger match = childMatchValue.getUnsignedValueIgnoreMask();
String partialMatch = childMatchValue.hasValue() ? "" : "*";
String matchStr =
match.equals(actual) ? " Match" : (" Failed (=0x" +
Long.toHexString(actual.longValue()) + ")");
String matchStr = match.equals(actual) ? " Match"
: (" Failed (=0x" + Long.toHexString(actual.longValue()) + ")");
int msb = baseRegSize - reg.getLeastSignificatBitInBaseRegister() - 1;
int lsb = msb - reg.getBitLength() + 1;
append(partialMatch + reg.getName() + "(" + lsb + "," + msb + ") == 0x" +
@ -719,7 +718,8 @@ public class SleighDebugLogger {
dumpGlobalSet(state, num, mask, value, null);
}
private void dumpGlobalSet(ConstructState state, int num, int mask, int value, Address setAddr) {
private void dumpGlobalSet(ConstructState state, int num, int mask, int value,
Address setAddr) {
byte[] maskActualValue = new byte[contextBaseRegister.getMinimumByteSize() * 2];
int byteOffset = num * 4;
@ -951,8 +951,7 @@ public class SleighDebugLogger {
public static String getFormattedBytes(byte[] value) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < value.length; i++) {
String byteStr =
StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8);
String byteStr = StringUtilities.pad(Integer.toBinaryString(value[i] & 0xff), '0', 8);
buf.append(byteStr);
if (i < (value.length - 1)) {
buf.append(" ");

View file

@ -42,6 +42,7 @@ import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.util.AddressLabelInfo;
import ghidra.program.model.util.ProcessorSymbolType;
import ghidra.sleigh.grammar.SourceFileIndexer;
import ghidra.sleigh.grammar.SleighPreprocessor;
import ghidra.util.*;
import ghidra.util.task.TaskMonitor;
@ -52,8 +53,18 @@ import utilities.util.FileUtilities;
public class SleighLanguage implements Language {
public static final int SLA_FORMAT_VERSION = 2; // What format of the .sla file this expects
// This value should always match SleighBase.SLA_FORMAT_VERSION
/**
* NOTE: The value of {@link SleighLanguage#SLA_FORMAT_VERSION} must match that of
* {@link ghidra.pcodeCPort.sleighbase.SleighBase#SLA_FORMAT_VERSION}!.
* <p>
* SLA_FORMAT_VERSION should be incremented whenever the format of the .sla
* files change.
* <p>
* Version 3: October 2020: added source file information for each constructor. <br>
* Version 2: April 2019: Changed numbering of Overlay spaces.<br>
* Version 1: Initial version.<br>
*/
public static final int SLA_FORMAT_VERSION = 3;
private Map<CompilerSpecID, SleighCompilerSpecDescription> compilerSpecDescriptions;
private HashMap<CompilerSpecID, BasicCompilerSpec> compilerSpecs;
private List<InjectPayloadSleigh> additionalInject = null;
@ -70,6 +81,7 @@ public class SleighLanguage implements Language {
private int defaultPointerWordSize = 1; // Default wordsize to send down with pointer data-types
private SleighLanguageDescription description;
private ParallelInstructionLanguageHelper parallelHelper;
private SourceFileIndexer indexer; //used to provide source file info for constructors
/**
* Symbols used by sleigh
@ -427,6 +439,14 @@ public class SleighLanguage implements Language {
return symtab;
}
/**
* Returns the source file indexer
* @return indexer
*/
public SourceFileIndexer getSourceFileIndexer() {
return indexer;
}
@Override
public void reloadLanguage(TaskMonitor monitor) throws IOException {
reloadLanguage(monitor, false);
@ -663,8 +683,7 @@ public class SleighLanguage implements Language {
while (!parser.peek().isEnd()) {
element = parser.start("properties", "segmented_address", "segmentop", "programcounter",
"data_space", "inferptrbounds", "context_data", "volatile", "jumpassist",
"incidentalcopy",
"register_data", "default_symbols", "default_memory_blocks");
"incidentalcopy", "register_data", "default_symbols", "default_memory_blocks");
if (element.getName().equals("properties")) {
while (!parser.peek().isEnd()) {
XmlElement next = parser.start("property");
@ -905,6 +924,8 @@ public class SleighLanguage implements Language {
if (numsecstr != null) {
numSections = SpecXmlUtils.decodeInt(numsecstr);
}
indexer = new SourceFileIndexer();
indexer.restoreXml(parser);
parseSpaces(parser);
symtab = new SymbolTable();
symtab.restoreXml(parser, this);

View file

@ -27,14 +27,18 @@ import ghidra.pcodeCPort.space.AddrSpace;
import ghidra.pcodeCPort.space.spacetype;
import ghidra.pcodeCPort.translate.Translate;
import ghidra.pcodeCPort.utils.XmlUtils;
import ghidra.sleigh.grammar.SourceFileIndexer;
public abstract class SleighBase extends Translate implements NamedSymbolProvider {
// NOTE: restoreXml method removed as it is only used by the decompiler's
// implementation
public static final int SLA_FORMAT_VERSION = 2; // What format of the .sla file this produces
// This value should always match SleighLanguage.SLA_FORMAT_VERSION
/**
* Note: The value of {@link SleighBase#SLA_FORMAT_VERSION} must match
* {@link ghidra.app.plugin.processors.sleigh.SleighLanguage#SLA_FORMAT_VERSION}.
*/
public static final int SLA_FORMAT_VERSION = 3;
private VectorSTL<String> userop = new VectorSTL<>();
private address_set varnode_xref = new address_set(); // Cross-reference registers by address
protected SubtableSymbol root;
@ -42,6 +46,8 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide
protected int maxdelayslotbytes; // Maximum number of bytes in a delayslot directive
protected int unique_allocatemask; // Bits that are guaranteed to be zero in the unique allocation scheme
protected int numSections; // Number of named sections
protected SourceFileIndexer indexer; //indexer for source files
//used to provide source file info for constructors
@Override
public SleighSymbol findSymbol(String nm) {
@ -61,6 +67,7 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide
maxdelayslotbytes = 0;
unique_allocatemask = 0;
numSections = 0;
indexer = new SourceFileIndexer();
}
public boolean isInitialized() {
@ -74,18 +81,21 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide
for (iter = glb.begin(); !iter.isEnd(); iter.increment()) {
SleighSymbol sym = iter.get();
if (sym.getType() == symbol_type.varnode_symbol) {
Pair<IteratorSTL<VarnodeSymbol>, Boolean> res = varnode_xref.insert((VarnodeSymbol) sym);
Pair<IteratorSTL<VarnodeSymbol>, Boolean> res =
varnode_xref.insert((VarnodeSymbol) sym);
if (!res.second) {
errorPairs.add(sym);
errorPairs.add(res.first.get());
}
} else if (sym.getType() == symbol_type.userop_symbol) {
}
else if (sym.getType() == symbol_type.userop_symbol) {
int index = ((UserOpSymbol) sym).getIndex();
while (userop.size() <= index) {
userop.push_back("");
}
userop.set(index, sym.getName());
} else if (sym.getType() == symbol_type.context_symbol) {
}
else if (sym.getType() == symbol_type.context_symbol) {
ContextSymbol csym = (ContextSymbol) sym;
ContextField field = (ContextField) csym.getPatternValue();
int startbit = field.getStartBit();
@ -189,13 +199,14 @@ public abstract class SleighBase extends Translate implements NamedSymbolProvide
XmlUtils.a_v_u(s, "numsections", numSections);
}
s.append(">\n");
indexer.saveXml(s);
s.append("<spaces");
XmlUtils.a_v(s, "defaultspace", getDefaultSpace().getName());
s.append(">\n");
for (int i = 0; i < numSpaces(); ++i) {
AddrSpace spc = getSpace(i);
if ((spc.getType() == spacetype.IPTR_CONSTANT) || (spc.getType() == spacetype.IPTR_FSPEC)
|| (spc.getType() == spacetype.IPTR_IOP)) {
if ((spc.getType() == spacetype.IPTR_CONSTANT) ||
(spc.getType() == spacetype.IPTR_FSPEC) || (spc.getType() == spacetype.IPTR_IOP)) {
continue;
}
spc.saveXml(s);

View file

@ -381,8 +381,7 @@ public class SleighCompile extends SleighBase {
final int symSize = sym.getSize();
if (symSize % 4 != 0) {
reportError(sym.location,
String.format(
"Invalid size of context register '%s' (%d); must be a multiple of 4",
String.format("Invalid size of context register '%s' (%d); must be a multiple of 4",
sym.getName(), symSize));
}
final int maxBits = symSize * 8 - 1;
@ -563,8 +562,7 @@ public class SleighCompile extends SleighBase {
noCollisions = false;
if (warnalllocalcollisions) {
reportWarning(ct.location,
String.format(
"Possible operand collision between symbols '%s' and '%s'",
String.format("Possible operand collision between symbols '%s' and '%s'",
ct.getOperand(collideOperand).getName(), ct.getOperand(i).getName()));
}
@ -649,7 +647,6 @@ public class SleighCompile extends SleighBase {
pcode.resetLabelCount();
}
public void reportError(Location location, String msg) {
entry("reportError", location, msg);
Msg.error(this, MessageFormattingUtils.format(location, msg));
@ -915,9 +912,8 @@ public class SleighCompile extends SleighBase {
insertSpace(spc);
if (qual.isdefault) {
if (getDefaultSpace() != null) {
reportError(location,
"Multiple default spaces -- '" + getDefaultSpace().getName() + "', '" +
qual.name + "'");
reportError(location, "Multiple default spaces -- '" + getDefaultSpace().getName() +
"', '" + qual.name + "'");
}
else {
setDefaultSpace(spc.getIndex()); // Make the flagged space
@ -1324,6 +1320,10 @@ public class SleighCompile extends SleighBase {
sym.addConstructor(curct);
symtab.addScope(); // Make a new symbol scope for our constructor
pcode.resetLabelCount();
Integer index = indexer.index(location);
if (index != null) {
curct.setSourceFileIndex(index);
}
return curct;
}
@ -1445,8 +1445,7 @@ public class SleighCompile extends SleighBase {
big.markSubtableOperands(check);
Pair<Integer, Location> res = cur.section.fillinBuild(check, getConstantSpace());
if (res.first == 1) {
myErrors.push_back(
scopeString + "Duplicate BUILD statements at " + res.second);
myErrors.push_back(scopeString + "Duplicate BUILD statements at " + res.second);
}
if (res.first == 2) {
myErrors.push_back(
@ -1454,8 +1453,7 @@ public class SleighCompile extends SleighBase {
}
if (!pcode.propagateSize(cur.section)) {
myErrors.push_back(
scopeString + "Could not resolve at least 1 variable size");
myErrors.push_back(scopeString + "Could not resolve at least 1 variable size");
}
}
if (i < 0) { // These potential errors only apply to main section

View file

@ -48,6 +48,7 @@ public class Constructor {
private int firstwhitespace; // Index of first whitespace piece in -printpiece-
private int flowthruindex; // if >=0 then print only a single operand no markup
private boolean inerror;
private int sourceFileIndex = -1; //source file index
public TokenPattern getPattern() {
return pattern;
@ -77,6 +78,22 @@ public class Constructor {
return location == null ? 0 : location.lineno;
}
/**
* Set the source file index
* @param index index
*/
public void setSourceFileIndex(int index) {
sourceFileIndex = index;
}
/**
* Return the source file index
* @return index
*/
public int getIndex() {
return sourceFileIndex;
}
public void addContext(VectorSTL<ContextChange> vec) {
context = vec;
}
@ -359,6 +376,8 @@ public class Constructor {
s.print(minimumlength);
s.append("\"");
s.append(" line=\"");
s.print(sourceFileIndex);
s.append(":");
s.print(getLineno());
s.append("\">\n");
for (int i = 0; i < operands.size(); ++i) {

View file

@ -0,0 +1,118 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.sleigh.grammar;
import java.io.PrintStream;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import ghidra.pcodeCPort.utils.XmlUtils;
import ghidra.util.Msg;
import ghidra.xml.XmlElement;
import ghidra.xml.XmlPullParser;
/**
* This class is used to index source files in a SLEIGH language module.
* The SLEIGH compiler records the index of the source file for a constructor rather
* than the file name. This is an optimization to avoid repeating the file name in
* the .sla files.
*/
public class SourceFileIndexer {
private BiMap<String, Integer> filenameToIndex;
private int leastUnusedIndex;
/**
* Creates a {code SourceFileIndexer} object with an empty index.
*/
public SourceFileIndexer() {
filenameToIndex = HashBiMap.create();
leastUnusedIndex = 0;
}
/**
* Adds the filename of a location to the index if it is not already present.
* @param loc location containing filename to add
* @return index associated with filename, or {@code null} if a {@code null} {@link Location}
* or a {@link Location} with a {@code null} filename was provided as input.
*/
public Integer index(Location loc) {
if (loc == null) {
Msg.info(this, "null Location");
return null;
}
String filename = loc.filename;
if (filename == null) {
Msg.info(this, "null filename");
return null;
}
Integer res = filenameToIndex.putIfAbsent(filename, leastUnusedIndex);
if (res == null) {
return leastUnusedIndex++;
}
return res;
}
/**
* Returns the index for a filename
* @param filename file
* @return index or {@code null} if {@code filename} is not in the index.
*/
public Integer getIndex(String filename) {
return filenameToIndex.get(filename);
}
/**
* Returns the file name at a given index
* @param index index
* @return file name or {@code null} if there is no file with that index
*/
public String getFileName(Integer index) {
return filenameToIndex.inverse().get(index);
}
/**
* Save the index as XML
* @param s stream to write to
*/
public void saveXml(PrintStream s) {
s.append("<sourcefiles>\n");
for (int i = 0; i < leastUnusedIndex; ++i) {
s.append("<sourcefile name=\"");
XmlUtils.xml_escape(s, filenameToIndex.inverse().get(i));
s.append("\" index=\"" + i + "\"/>\n");
}
s.append("</sourcefiles>\n");
}
/**
* Restore an index saved as to XML
* @param parser xml parser
*/
public void restoreXml(XmlPullParser parser) {
XmlElement elem = parser.start("sourcefiles");
XmlElement subElem = null;
while ((subElem = parser.softStart("sourcefile")) != null) {
String filename = subElem.getAttribute("name");
Integer index = Integer.parseInt(subElem.getAttribute("index"));
filenameToIndex.put(filename, index);
parser.end(subElem);
}
parser.end(elem);
}
}

View file

@ -0,0 +1,63 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.sleigh.grammar;
import static org.junit.Assert.*;
import org.junit.Test;
import generic.test.AbstractGenericTest;
public class SourceFileIndexerTest extends AbstractGenericTest {
@Test
public void basicTest() {
Location loc1_1 = new Location("file1", 1);
Location loc1_2 = new Location("file1", 2);
Location loc2_1 = new Location("file2", 11);
Location loc2_2 = new Location("file2", 22);
Location loc3_1 = new Location("file3", 111);
Location loc3_2 = new Location("file3", 222);
Location nullLocation = null;
Location nullFilename = new Location(null, 1000);
SourceFileIndexer indexer = new SourceFileIndexer();
indexer.index(loc1_1);
indexer.index(loc1_2);
indexer.index(loc3_2);
int ret2_1 = indexer.index(loc2_1);
indexer.index(nullLocation);
indexer.index(loc3_1);
indexer.index(nullFilename);
int ret2_2 = indexer.index(loc2_2);
assertEquals(ret2_1, ret2_2);
int file1_index = indexer.getIndex(loc1_1.filename);
assertEquals(file1_index, indexer.getIndex(loc1_2.filename).intValue());
int file2_index = indexer.getIndex(loc2_1.filename);
assertEquals(file2_index, indexer.getIndex(loc2_2.filename).intValue());
int file3_index = indexer.getIndex(loc3_1.filename);
assertEquals(file3_index, indexer.getIndex(loc3_2.filename).intValue());
assertNotEquals(file1_index, file2_index);
assertNotEquals(file1_index, file3_index);
assertNotEquals(file2_index, file3_index);
}
}