{
}
return true;
}
+
+ /**
+ * Returns the source file
+ * @return source file
+ */
+ public String getSourceFile() {
+ return sourceFile;
+ }
}
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java
index d397b6df28..7eeaec4132 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighDebugLogger.java
@@ -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(" ");
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
index f45f183e38..b5a3be9dfd 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/plugin/processors/sleigh/SleighLanguage.java
@@ -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}!.
+ *
+ * SLA_FORMAT_VERSION should be incremented whenever the format of the .sla
+ * files change.
+ *
+ * Version 3: October 2020: added source file information for each constructor.
+ * Version 2: April 2019: Changed numbering of Overlay spaces.
+ * Version 1: Initial version.
+ */
+ public static final int SLA_FORMAT_VERSION = 3;
private Map compilerSpecDescriptions;
private HashMap compilerSpecs;
private List 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");
@@ -907,6 +926,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);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java
index 7a7cf2e32a..8b0f02b056 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/sleighbase/SleighBase.java
@@ -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 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, Boolean> res = varnode_xref.insert((VarnodeSymbol) sym);
+ Pair, 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("\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);
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java
index ad779e50bc..4e5bb2d993 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/SleighCompile.java
@@ -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));
@@ -922,9 +919,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
@@ -1331,6 +1327,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;
}
@@ -1452,8 +1452,7 @@ public class SleighCompile extends SleighBase {
big.markSubtableOperands(check);
Pair 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(
@@ -1461,8 +1460,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
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java
index 69b52932a9..f61bce837c 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/Constructor.java
@@ -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 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) {
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java
new file mode 100644
index 0000000000..00fea56d76
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/sleigh/grammar/SourceFileIndexer.java
@@ -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 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("\n");
+ for (int i = 0; i < leastUnusedIndex; ++i) {
+ s.append("\n");
+ }
+ s.append("\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);
+ }
+
+}
diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java
new file mode 100644
index 0000000000..f4191422ab
--- /dev/null
+++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/sleigh/grammar/SourceFileIndexerTest.java
@@ -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);
+ }
+
+}