From 33f1f38292dae57e4af2bceb614ffe82742d8a54 Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Sat, 30 Aug 2025 13:11:34 -0400 Subject: [PATCH] Test fixes --- .../app/util/exporter/ProgramTextOptions.java | 8 +- .../util/exporter/ReferenceLineDispenser.java | 109 ++++++++++-------- .../app/util/exporter/StringComparer.java | 33 +++++- .../app/util/viewer/field/XrefViewerTest.java | 22 ++-- 4 files changed, 101 insertions(+), 71 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextOptions.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextOptions.java index b06b001e0a..c1654639ec 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextOptions.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ProgramTextOptions.java @@ -4,9 +4,9 @@ * 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. @@ -57,13 +57,13 @@ class ProgramTextOptions { private final static int DEFAULT_OPERAND_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_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_NAME_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_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 String DEFAULT_LABEL_SUFFIX = ":"; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ReferenceLineDispenser.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ReferenceLineDispenser.java index 710741bc0f..aa804900c8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ReferenceLineDispenser.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/ReferenceLineDispenser.java @@ -32,6 +32,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser { private String header; private Memory memory; private ReferenceManager referenceManager; + private boolean forwardRefs; private List lines = new ArrayList<>(); @@ -50,11 +51,11 @@ class ReferenceLineDispenser extends AbstractLineDispenser { this.fillAmount = options.getAddrWidth() + options.getBytesWidth() + options.getLabelWidth(); this.isHTML = options.isHTML(); + this.forwardRefs = forwardRefs; List refs = (forwardRefs ? getForwardRefs(cu) : getXRefList(cu)); List offcuts = (forwardRefs ? List.of() : getOffcutXRefList(cu)); - - processRefs(cu.getMinAddress(), forwardRefs, refs, offcuts); + processRefs(cu.getMinAddress(), refs, offcuts); } ReferenceLineDispenser(Variable var, Program program, ProgramTextOptions options) { @@ -69,6 +70,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser { options.getStackVarDataTypeWidth() + options.getStackVarOffsetWidth() + options.getStackVarCommentWidth(); this.isHTML = options.isHTML(); + this.forwardRefs = false; List xrefs = new ArrayList<>(); List offcuts = new ArrayList<>(); @@ -80,7 +82,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser { xrefs.sort(comparator); offcuts.sort(comparator); - processRefs(var.getFunction().getEntryPoint(), false, xrefs, offcuts); + processRefs(var.getFunction().getEntryPoint(), xrefs, offcuts); } @Override @@ -133,8 +135,9 @@ class ReferenceLineDispenser extends AbstractLineDispenser { return refs; } - private void processRefs(Address addr, boolean isForward, List refs, + private void processRefs(Address addr, List refs, List offcuts) { + if (width < 1) { return; } @@ -142,7 +145,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser { return; } - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); List all = new ArrayList<>(); all.addAll(refs); all.addAll(offcuts); @@ -160,60 +163,41 @@ class ReferenceLineDispenser extends AbstractLineDispenser { 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 refsInCurrLine = 0; - + int currentXrefWidth = 0; 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); - Address address = isForward ? ref.getToAddress() : ref.getFromAddress(); - boolean isInMem = memory.contains(address); - if (isHTML && isInMem) { - buf.append(""); - } - buf.append(address); + XrefItem xrefItem = new XrefItem(ref); - String refType = getRefTypeDisplayString(ref); - buf.append(refType); - - if (isHTML && isInMem) { - buf.append(""); - } - - refsInCurrLine++; - - if (refsInCurrLine == refsPerLine) { - lines.add((displayRefHeader ? prefix : "") + buf.toString()); + int nextWidth = currentXrefWidth + xrefItem.getDisplayableWidth(); + if (nextWidth > width) { + // line is too long for the current xref, break + lines.add(prefix + buf.toString()); 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) { - lines.add((displayRefHeader ? prefix : "") + buf.toString()); - buf.delete(0, buf.length()); + // add the last xref line + if (buf.length() != 0) { + lines.add(prefix + buf.toString()); } } @@ -295,4 +279,29 @@ class ReferenceLineDispenser extends AbstractLineDispenser { }); 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 "%s".formatted(href, displayableString); + } + + return displayableString; + } + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/StringComparer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/StringComparer.java index 9c265ff308..19421f4be0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/StringComparer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/exporter/StringComparer.java @@ -4,9 +4,9 @@ * 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. @@ -24,10 +24,11 @@ import ghidra.util.Msg; public class StringComparer { public static void compareLines(List expectedList, File actualFile) throws Exception { + + FilePrinter filePrinter = new FilePrinter(actualFile); + int index = 0; - boolean hasFailure = false; - try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) { int excess = 0; while (true) { @@ -51,17 +52,21 @@ public class StringComparer { hasFailure |= !match; if (!match) { - Msg.debug(StringComparer.class, "Expected line does not match actual line (" + index + - "): \nExpected: " + expectedLine + "\nActual: " + actualLine); + filePrinter.print(); + Msg.debug(StringComparer.class, + "Expected line does not match actual line (" + index + + "): \nExpected: " + expectedLine + "\nActual: " + actualLine); } } if (excess > 0) { + filePrinter.print(); String message = "Actual file contains " + excess + " more lines than expected"; Msg.debug(StringComparer.class, message); Assert.fail(message); } else if (!hasFailure && index < expectedList.size()) { + filePrinter.print(); int fewer = expectedList.size() - index; String message = "Actual file contains " + fewer + " 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; + } + } + } } diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/XrefViewerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/XrefViewerTest.java index f16497c397..c7750516e5 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/XrefViewerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/XrefViewerTest.java @@ -4,9 +4,9 @@ * 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. @@ -138,18 +138,18 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest { /* Direct References - + 01001009 ?? LAB_01001007 READ 01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL 01001050 thunk_FUN_01001005 ?? FUN_01001005 THUNK - - + + References to the Thunk Function - + 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk */ - doubleClickXRef(baseFunctionAddress, "XREF[1]: "); + doubleClickXRef(baseFunctionAddress, "XREF[2]: "); ComponentProvider comp = waitForComponentProvider(TableComponentProvider.class); TableComponentProvider tableProvider = (TableComponentProvider) comp; GhidraProgramTableModel model = tableProvider.getModel(); @@ -176,12 +176,12 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest { /* Direct References - + 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk - - + + References to the thunk and the end thunked function - + 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk 01001009 ?? LAB_01001007 READ 01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL