Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz 2025-09-02 05:53:32 -04:00
commit 1ca9e32a57
4 changed files with 91 additions and 61 deletions

View file

@ -57,13 +57,13 @@ class ProgramTextOptions {
private final static int DEFAULT_OPERAND_WIDTH = 40; private final static int DEFAULT_OPERAND_WIDTH = 40;
private final static int DEFAULT_EOL_WIDTH = 40; private final static int DEFAULT_EOL_WIDTH = 40;
private final static int DEFAULT_REF_HEADER_WIDTH = 13; private final static int DEFAULT_REF_HEADER_WIDTH = 13;
private final static int DEFAULT_REF_WIDTH = 40; private final static int DEFAULT_REF_WIDTH = 50; // about 4 refs per line
private final static int DEFAULT_STACK_VAR_PRENAME_WIDTH = 10; private final static int DEFAULT_STACK_VAR_PRENAME_WIDTH = 10;
private final static int DEFAULT_STACK_VAR_NAME_WIDTH = 15; private final static int DEFAULT_STACK_VAR_NAME_WIDTH = 15;
private final static int DEFAULT_STACK_VAR_DATATYPE_WIDTH = 15; private final static int DEFAULT_STACK_VAR_DATATYPE_WIDTH = 15;
private final static int DEFAULT_STACK_VAR_OFFSET_WIDTH = 8; private final static int DEFAULT_STACK_VAR_OFFSET_WIDTH = 8;
private final static int DEFAULT_STACK_VAR_COMMENT_WIDTH = 20; private final static int DEFAULT_STACK_VAR_COMMENT_WIDTH = 20;
private final static int DEFAULT_STACK_VAR_XREF_WIDTH = 50; private final static int DEFAULT_STACK_VAR_XREF_WIDTH = 60; // about 5 refs per line
private final static int DEFAULT_DATA_FIELD_NAME_WIDTH = 12; private final static int DEFAULT_DATA_FIELD_NAME_WIDTH = 12;
private final static String DEFAULT_LABEL_SUFFIX = ":"; private final static String DEFAULT_LABEL_SUFFIX = ":";

View file

@ -32,6 +32,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
private String header; private String header;
private Memory memory; private Memory memory;
private ReferenceManager referenceManager; private ReferenceManager referenceManager;
private boolean forwardRefs;
private List<String> lines = new ArrayList<>(); private List<String> lines = new ArrayList<>();
@ -50,11 +51,11 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
this.fillAmount = this.fillAmount =
options.getAddrWidth() + options.getBytesWidth() + options.getLabelWidth(); options.getAddrWidth() + options.getBytesWidth() + options.getLabelWidth();
this.isHTML = options.isHTML(); this.isHTML = options.isHTML();
this.forwardRefs = forwardRefs;
List<Reference> refs = (forwardRefs ? getForwardRefs(cu) : getXRefList(cu)); List<Reference> refs = (forwardRefs ? getForwardRefs(cu) : getXRefList(cu));
List<Reference> offcuts = (forwardRefs ? List.of() : getOffcutXRefList(cu)); List<Reference> offcuts = (forwardRefs ? List.of() : getOffcutXRefList(cu));
processRefs(cu.getMinAddress(), refs, offcuts);
processRefs(cu.getMinAddress(), forwardRefs, refs, offcuts);
} }
ReferenceLineDispenser(Variable var, Program program, ProgramTextOptions options) { ReferenceLineDispenser(Variable var, Program program, ProgramTextOptions options) {
@ -69,6 +70,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
options.getStackVarDataTypeWidth() + options.getStackVarOffsetWidth() + options.getStackVarDataTypeWidth() + options.getStackVarOffsetWidth() +
options.getStackVarCommentWidth(); options.getStackVarCommentWidth();
this.isHTML = options.isHTML(); this.isHTML = options.isHTML();
this.forwardRefs = false;
List<Reference> xrefs = new ArrayList<>(); List<Reference> xrefs = new ArrayList<>();
List<Reference> offcuts = new ArrayList<>(); List<Reference> offcuts = new ArrayList<>();
@ -80,7 +82,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
xrefs.sort(comparator); xrefs.sort(comparator);
offcuts.sort(comparator); offcuts.sort(comparator);
processRefs(var.getFunction().getEntryPoint(), false, xrefs, offcuts); processRefs(var.getFunction().getEntryPoint(), xrefs, offcuts);
} }
@Override @Override
@ -133,8 +135,9 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
return refs; return refs;
} }
private void processRefs(Address addr, boolean isForward, List<Reference> refs, private void processRefs(Address addr, List<Reference> refs,
List<Reference> offcuts) { List<Reference> offcuts) {
if (width < 1) { if (width < 1) {
return; return;
} }
@ -142,7 +145,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
return; return;
} }
StringBuffer buf = new StringBuffer(); StringBuilder buf = new StringBuilder();
List<Reference> all = new ArrayList<>(); List<Reference> all = new ArrayList<>();
all.addAll(refs); all.addAll(refs);
all.addAll(offcuts); all.addAll(offcuts);
@ -160,60 +163,41 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
buf.append(clip(text, headerWidth)); buf.append(clip(text, headerWidth));
} }
} }
else {
buf.append(getFill(headerWidth));
buf.append(prefix);
}
int refsPerLine = width / (all.get(0).toString().length() + XREFS_DELIM.length()); int currentXrefWidth = 0;
int refsInCurrLine = 0;
for (int i = 0; i < all.size(); ++i) { for (int i = 0; i < all.size(); ++i) {
//if we are not displaying the xref header,
//then we need to append the comment prefix
if (i == 0 && !displayRefHeader) {
buf.append(getFill(headerWidth));
buf.append(prefix);
}
//if we have started a new line, then
//we need to append the comment prefix
if (refsInCurrLine == 0 && i != 0) {
buf.append(getFill(headerWidth));
if (!displayRefHeader) {
buf.append(prefix);
}
}
//if we already appended a ref to the line
//and we are about to append one more,
//then we need the delim
if (refsInCurrLine > 0) {
buf.append(XREFS_DELIM);
}
//does memory contain this address? if so, then hyperlink it // does memory contain this address? if so, then hyperlink it
Reference ref = all.get(i); Reference ref = all.get(i);
Address address = isForward ? ref.getToAddress() : ref.getFromAddress(); XrefItem xrefItem = new XrefItem(ref);
boolean isInMem = memory.contains(address);
if (isHTML && isInMem) {
buf.append("<A HREF=\"#" + getUniqueAddressString(address) + "\">");
}
buf.append(address);
String refType = getRefTypeDisplayString(ref); int nextWidth = currentXrefWidth + xrefItem.getDisplayableWidth();
buf.append(refType); if (nextWidth > width) {
// line is too long for the current xref, break
if (isHTML && isInMem) { lines.add(prefix + buf.toString());
buf.append("</A>");
}
refsInCurrLine++;
if (refsInCurrLine == refsPerLine) {
lines.add((displayRefHeader ? prefix : "") + buf.toString());
buf.delete(0, buf.length()); buf.delete(0, buf.length());
refsInCurrLine = 0;
// since we already have the next xref, add the next line's prefix
buf.append(getFill(headerWidth));
currentXrefWidth = 0;
}
currentXrefWidth += xrefItem.getDisplayableWidth();
buf.append(xrefItem.getRawText());
if (i < all.size() - 1) {
buf.append(XREFS_DELIM);
} }
} }
if (refsInCurrLine > 0) { // add the last xref line
lines.add((displayRefHeader ? prefix : "") + buf.toString()); if (buf.length() != 0) {
buf.delete(0, buf.length()); lines.add(prefix + buf.toString());
} }
} }
@ -295,4 +279,29 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
}); });
return offcutList; return offcutList;
} }
private class XrefItem {
private Address address;
private String displayableString;
XrefItem(Reference ref) {
address = forwardRefs ? ref.getToAddress() : ref.getFromAddress();
String refType = getRefTypeDisplayString(ref);
this.displayableString = address.toString() + refType;
}
int getDisplayableWidth() {
return displayableString.length();
}
String getRawText() {
boolean isInMem = memory.contains(address);
if (isHTML && isInMem) {
String href = getUniqueAddressString(address);
return "<A HREF=\"#%s\">%s</A>".formatted(href, displayableString);
}
return displayableString;
}
}
} }

View file

@ -24,10 +24,11 @@ import ghidra.util.Msg;
public class StringComparer { public class StringComparer {
public static void compareLines(List<String> expectedList, File actualFile) throws Exception { public static void compareLines(List<String> expectedList, File actualFile) throws Exception {
FilePrinter filePrinter = new FilePrinter(actualFile);
int index = 0; int index = 0;
boolean hasFailure = false; boolean hasFailure = false;
try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) {
int excess = 0; int excess = 0;
while (true) { while (true) {
@ -51,17 +52,21 @@ public class StringComparer {
hasFailure |= !match; hasFailure |= !match;
if (!match) { if (!match) {
Msg.debug(StringComparer.class, "Expected line does not match actual line (" + index + filePrinter.print();
"): \nExpected: " + expectedLine + "\nActual: " + actualLine); Msg.debug(StringComparer.class,
"Expected line does not match actual line (" + index +
"): \nExpected: " + expectedLine + "\nActual: " + actualLine);
} }
} }
if (excess > 0) { if (excess > 0) {
filePrinter.print();
String message = "Actual file contains " + excess + " more lines than expected"; String message = "Actual file contains " + excess + " more lines than expected";
Msg.debug(StringComparer.class, message); Msg.debug(StringComparer.class, message);
Assert.fail(message); Assert.fail(message);
} }
else if (!hasFailure && index < expectedList.size()) { else if (!hasFailure && index < expectedList.size()) {
filePrinter.print();
int fewer = expectedList.size() - index; int fewer = expectedList.size() - index;
String message = "Actual file contains " + fewer + String message = "Actual file contains " + fewer +
" fewer lines than expected"; " fewer lines than expected";
@ -74,4 +79,20 @@ public class StringComparer {
} }
} }
} }
private static class FilePrinter {
private File f;
private boolean printed;
FilePrinter(File f) {
this.f = f;
}
void print() {
if (!printed) {
Msg.debug(this, "Test file: " + f);
printed = true;
}
}
}
} }

View file

@ -153,7 +153,7 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk
*/ */
doubleClickXRef(baseFunctionAddress, "XREF[1]: "); doubleClickXRef(baseFunctionAddress, "XREF[2]: ");
ComponentProvider comp = waitForComponentProvider(TableComponentProvider.class); ComponentProvider comp = waitForComponentProvider(TableComponentProvider.class);
TableComponentProvider<?> tableProvider = (TableComponentProvider<?>) comp; TableComponentProvider<?> tableProvider = (TableComponentProvider<?>) comp;
GhidraProgramTableModel<?> model = tableProvider.getModel(); GhidraProgramTableModel<?> model = tableProvider.getModel();