GP-1717 - Fixed Plate Comment rending bugs

This commit is contained in:
dragonmacher 2022-01-28 19:06:10 -05:00
parent c3c20d9e0f
commit 087529c448
5 changed files with 247 additions and 253 deletions

View File

@ -21,11 +21,7 @@ import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.tools.Diagnostic.Kind;
import db.DBHandle;
import ghidra.util.database.DBCachedDomainObjectAdapter;
import ghidra.util.database.DBOpenMode;
import ghidra.util.database.annot.DBAnnotatedField;
import ghidra.util.task.TaskMonitor;
public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
final VariableElement field;
@ -127,31 +123,8 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
return codecElem;
}
class A extends DBCachedDomainObjectAdapter {
protected A(DBHandle dbh, DBOpenMode openMode, TaskMonitor monitor, String name,
int timeInterval, int bufSize, Object consumer) {
super(dbh, openMode, monitor, name, timeInterval, bufSize, consumer);
// TODO Auto-generated constructor stub
}
@Override
public boolean isChangeable() {
// TODO Auto-generated method stub
return false;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return null;
}
}
protected void checkCodecTypes(TypeElement objectType) {
//experiment(new Blargh(null, null));
TypeElement codecType = getCodecTypeElement();
if (codecType == null) {
ctx.messager.printMessage(Kind.ERROR,
@ -169,13 +142,13 @@ public class DBAnnotatedFieldValidator extends AbstractDBAnnotationValidator {
// 3) FieldType is non-abstract
// 4) The codec has an appropriate constructor
for (Element enc : codecType.getEnclosedElements()) {
if (enc.getKind() == ElementKind.CONSTRUCTOR) {
ExecutableElement exe = (ExecutableElement) enc;
ExecutableType exeType = (ExecutableType) exe.asType();
//throw new RuntimeException();
}
}
// for (Element enc : codecType.getEnclosedElements()) {
// if (enc.getKind() == ElementKind.CONSTRUCTOR) {
// ExecutableElement exe = (ExecutableElement) enc;
// ExecutableType exeType = (ExecutableType) exe.asType();
// //throw new RuntimeException();
// }
// }
Map<String, TypeMirror> args = ctx.getArguments(codecType, ctx.DB_FIELD_CODEC_ELEM);

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,38 +25,42 @@ import ghidra.app.util.viewer.proxy.ProxyObj;
* the browser needs from the fields.
*/
public interface ListingField extends Field {
/**
* Returns the FieldFactory that generated this Field
*/
/**
* Returns the FieldFactory that generated this Field
* @return the FieldFactory that generated this Field
*/
public FieldFactory getFieldFactory();
/**
* Returns the height above the imaginary base line used for alignment of
* fields.
*/
int getHeightAbove();
/**
* Returns the height below the imaginary base line used for alignment of
* fields.
*/
int getHeightBelow();
/**
* Returns the fieldModel that has the FieldFactory that generated this field.
*/
/**
* Returns the height above the imaginary base line used for alignment of fields.
*/
@Override
public int getHeightAbove();
/**
* Returns the height below the imaginary base line used for alignment of fields.
*/
@Override
public int getHeightBelow();
/**
* Returns the fieldModel that has the FieldFactory that generated this field.
* @return the fieldModel that has the FieldFactory that generated this field.
*/
public FieldFormatModel getFieldModel();
/**
* Returns the object that the fieldFactory used to generate the information
* in this field.
*/
public ProxyObj getProxy();
/**
* Returns the object that the fieldFactory used to generate the information in this field.
* @return the object that the fieldFactory used to generate the information in this field.
*/
public ProxyObj<?> getProxy();
/**
* Returns the object that was clicked on a Field for the given FieldLocation. This may be the
* field itself or a lower-level entity, such as a FieldElement.
* field itself or a lower-level entity, such as a FieldElement.
*
* @param fieldLocation The location that was clicked.
* @return the object that was clicked
*/
public Object getClickedObject( FieldLocation fieldLocation );
public Object getClickedObject(FieldLocation fieldLocation);
}

View File

@ -57,31 +57,31 @@ public class PlateFieldFactory extends FieldFactory {
*/
private static final int CONTENT_PADDING = 4;
private static final String ELLIPSIS = "...";
final static String FUNCTION_PLATE_COMMENT = " FUNCTION";
final static String THUNK_FUNCTION_PLATE_COMMENT = " THUNK FUNCTION";
final static String POINTER_TO_EXTERNAL_FUNCTION_COMMENT = " POINTER to EXTERNAL FUNCTION";
final static String POINTER_TO_NONEXTERNAL_FUNCTION_COMMENT = " POINTER to FUNCTION";
final static String CASE_PLATE_COMMENT = " CASE";
final static String EXT_ENTRY_PLATE_COMMENT = " EXTERNAL ENTRY";
final static String DEAD_CODE_PLATE_COMMENT = " DEAD";
final static String SUBROUTINE_PLATE_COMMENT = " SUBROUTINE";
final static String DEFAULT_PLATE_COMMENT = " ";
public final static String FUNCTION_PLATE_COMMENT = "FUNCTION";
private static final String THUNK_FUNCTION_PLATE_COMMENT = "THUNK FUNCTION";
private static final String POINTER_TO_EXTERNAL_FUNCTION_COMMENT =
"POINTER to EXTERNAL FUNCTION";
private static final String POINTER_TO_NONEXTERNAL_FUNCTION_COMMENT = "POINTER to FUNCTION";
static final String EXT_ENTRY_PLATE_COMMENT = "EXTERNAL ENTRY";
static final String DEAD_CODE_PLATE_COMMENT = "DEAD";
static final String SUBROUTINE_PLATE_COMMENT = "SUBROUTINE";
static final String DEFAULT_PLATE_COMMENT = " ";
final static String GROUP_TITLE = "Format Code";
final static String SHOW_SUBROUTINE_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + " Show Subroutine Plates";
final static String SHOW_FUNCTION_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + " Show Function Plates";
final static String SHOW_TRANSITION_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + " Show Transition Plates";
final static String SHOW_EXT_ENTRY_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + " Show External Entry Plates";
private static final String GROUP_TITLE = "Format Code";
static final String SHOW_SUBROUTINE_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + "Show Subroutine Plates";
static final String SHOW_FUNCTION_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + "Show Function Plates";
static final String SHOW_TRANSITION_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + "Show Transition Plates";
static final String SHOW_EXT_ENTRY_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + "Show External Entry Plates";
final static String LINES_BEFORE_FUNCTIONS_OPTION =
static final String LINES_BEFORE_FUNCTIONS_OPTION =
GROUP_TITLE + Options.DELIMITER + "Lines Before Functions";
final static String LINES_BEFORE_LABELS_OPTION =
static final String LINES_BEFORE_LABELS_OPTION =
GROUP_TITLE + Options.DELIMITER + "Lines Before Labels";
final static String LINES_BEFORE_PLATES_OPTION =
static final String LINES_BEFORE_PLATES_OPTION =
GROUP_TITLE + Options.DELIMITER + "Lines Before Plates";
private boolean initialized;
@ -91,7 +91,6 @@ public class PlateFieldFactory extends FieldFactory {
private boolean showSubroutinePlates;
private boolean showTransitionPlates;
private boolean showExternalPlates;
// private boolean showCasePlates;
private boolean showExternalFunctionPointerPlates;
private boolean showNonExternalFunctionPointerPlates;
@ -101,9 +100,6 @@ public class PlateFieldFactory extends FieldFactory {
private int nLinesBeforePlates;
private boolean isWordWrap;
/**
* Constructor
*/
public PlateFieldFactory() {
super(FIELD_NAME);
}
@ -120,7 +116,6 @@ public class PlateFieldFactory extends FieldFactory {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
init(fieldOptions);
// showCasePlates = fieldOptions.getBoolean(name, SHOW_CASE_PLATES_OPTION, false);
isWordWrap = fieldOptions.getBoolean(ENABLE_WORD_WRAP_MSG, false);
showExternalPlates = fieldOptions.getBoolean(SHOW_EXT_ENTRY_PLATES_OPTION, false);
showFunctionPlates = fieldOptions.getBoolean(SHOW_FUNCTION_PLATES_OPTION, true);
@ -239,17 +234,16 @@ public class PlateFieldFactory extends FieldFactory {
// add top border
elements.add(new TextFieldElement(asteriscs, row++, 0));
int commentsStart = row;
// add and word wrap the comments
List<FieldElement> commentsList = new ArrayList<>();
for (String c : comments) {
FieldElement element = CommentUtils.parseTextForAnnotations(c, p, prototype, row++);
elements.add(element);
commentsList.add(CommentUtils.parseTextForAnnotations(c, p, prototype, row++));
}
if (isWordWrap) {
elements = FieldUtils.wordWrapList(new CompositeFieldElement(elements), width);
commentsList = FieldUtils.wordWrapList(new CompositeFieldElement(commentsList), width);
}
boolean isClipped = addSideBorders(elements, commentsStart);
boolean isClipped = addSideBorders(commentsList);
elements.addAll(commentsList);
// add bottom border
elements.add(new TextFieldElement(asteriscs, row++, 0));
@ -257,33 +251,13 @@ public class PlateFieldFactory extends FieldFactory {
return isClipped;
}
/**
* Generate a text line for the plate text.
* Text will be left justified between two '*' and padded based upon the
* available field width.
* @param elements the field elements that may get updated
* @param commentsStart the start index of comment elements in the element list
* @return formatted plate text line
*/
private boolean addSideBorders(List<FieldElement> elements, int commentsStart) {
private boolean addSideBorders(List<FieldElement> comments) {
boolean isClipped = false;
// If there is only a single line comment, then center it
int n = elements.size() - commentsStart;
if (n == 1) {
FieldElement element = elements.get(0);
if (element.length() > 1 && element.charAt(0) == ' ') { // not sure why the space matters
FieldElementResult result = addSideBorder(element.substring(1), 1, true);
isClipped = result.isClipped();
elements.set(0, result.getFieldElement());
return isClipped;
}
}
for (int i = commentsStart; i < elements.size(); i++) {
FieldElementResult result = addSideBorder(elements.get(i), i, false);
for (int i = 0; i < comments.size(); i++) {
FieldElementResult result = addSideBorder(comments.get(i), i, false);
isClipped |= result.isClipped();
elements.set(i, result.getFieldElement());
comments.set(i, result.getFieldElement());
}
return isClipped;
}
@ -341,7 +315,7 @@ public class PlateFieldFactory extends FieldFactory {
private void addBlankLines(List<FieldElement> elements, int numberBlankLines, CodeUnit cu) {
AttributedString prototype = new AttributedString(EMPTY_STRING, color, getMetrics());
for (int row = 0; row < numberBlankLines; row++) {
elements.add(new TextFieldElement(prototype, row, 0));
elements.add(0, new TextFieldElement(prototype, row, 0));
}
}
@ -369,14 +343,16 @@ public class PlateFieldFactory extends FieldFactory {
}
AttributedString asteriscs = getStarsString();
int row = elements.size();
int row = elements.size(); // blank lines
// top border
elements.add(0, new TextFieldElement(asteriscs, row++, 0));
elements.add(new TextFieldElement(asteriscs, row++, 0));
int commentRow = row++;
AttributedString as = new AttributedString(defaultComment, color, getMetrics());
elements.add(new TextFieldElement(as, row++, 0));
addSideBorders(elements, 1);
TextFieldElement commentElement = new TextFieldElement(as, commentRow, 0);
FieldElementResult result = addSideBorder(commentElement, commentRow, true);
elements.add(result.getFieldElement());
// bottom border
elements.add(new TextFieldElement(asteriscs, row++, 0));
@ -470,13 +446,15 @@ public class PlateFieldFactory extends FieldFactory {
@Override
public ProgramLocation getProgramLocation(int row, int col, ListingField listingField) {
Object proxyObject = listingField.getProxy().getObject();
// if on a function, get the code unit there.
Object proxyObject = listingField.getProxy().getObject();
if (proxyObject instanceof Function) {
Function func = (Function) proxyObject;
Listing listing = func.getProgram().getListing();
proxyObject = listing.getCodeUnitAt(func.getEntryPoint());
}
if (!(proxyObject instanceof CodeUnit)) {
return null;
}
@ -698,12 +676,6 @@ public class PlateFieldFactory extends FieldFactory {
}
initialized = true;
StringBuilder sb = new StringBuilder();
sb.append("\n");
for (int i = 0; i < 19; i++) {
sb.append("|");
}
HelpLocation help = new HelpLocation(HelpTopics.CODE_BROWSER, "Format_Code");
options.getOptions(GROUP_TITLE).setOptionsHelpLocation(help);
@ -719,8 +691,6 @@ public class PlateFieldFactory extends FieldFactory {
"Toggle for whether a plate comment should be displayed for subroutines.");
options.registerOption(SHOW_FUNCTION_PLATES_OPTION, true, help,
"Toggle for whether a plate comment should be displayed for functions.");
// options.registerOption(SHOW_CASE_PLATES_OPTION,false, help,
// "Toggle for whether a plate comment should be displayed for a case statement.");
options.registerOption(SHOW_TRANSITION_PLATES_OPTION, false, help,
"Toggle for whether a plate comment should be displayed for a change " +
"in the flow type between instructions, when data follows " +
@ -747,9 +717,9 @@ public class PlateFieldFactory extends FieldFactory {
// Inner Classes
//==================================================================================================
private class PlateListingTextField extends ListingTextField {
class PlateListingTextField extends ListingTextField {
protected PlateListingTextField(ProxyObj<?> proxy, PlateFieldTextField field) {
PlateListingTextField(ProxyObj<?> proxy, PlateFieldTextField field) {
super(PlateFieldFactory.this, proxy, field);
}
@ -758,12 +728,12 @@ public class PlateFieldFactory extends FieldFactory {
}
}
private class PlateFieldTextField extends VerticalLayoutTextField {
class PlateFieldTextField extends VerticalLayoutTextField {
private boolean isCommentClipped;
private String commentText;
public PlateFieldTextField(List<FieldElement> textElements, PlateFieldFactory factory,
PlateFieldTextField(List<FieldElement> textElements, PlateFieldFactory factory,
ProxyObj<?> proxy, int startX, int width, String commentText,
boolean isCommentClipped) {
super(textElements, startX, width, Integer.MAX_VALUE,
@ -779,9 +749,16 @@ public class PlateFieldFactory extends FieldFactory {
@Override
public String getTextWithLineSeparators() {
// note: this is the comment text which will be blank for default plate comments
return commentText;
}
@Override
protected List<String> getLines() {
// open up access for testing
return super.getLines();
}
int getLeadingFillerLineCount() {
int count = 0;

View File

@ -20,8 +20,7 @@ import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.StringUtils;
import org.junit.*;
import docking.widgets.table.GTable;
@ -36,6 +35,8 @@ import ghidra.app.plugin.core.navigation.NextPrevAddressPlugin;
import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.plugin.core.table.TableServicePlugin;
import ghidra.app.services.GoToService;
import ghidra.app.util.viewer.field.PlateFieldFactory.PlateFieldTextField;
import ghidra.app.util.viewer.field.PlateFieldFactory.PlateListingTextField;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramDB;
@ -44,7 +45,6 @@ import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.test.*;
import ghidra.util.exception.AssertException;
import ghidra.util.table.GhidraProgramTableModel;
public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
@ -116,15 +116,8 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
Address addr = symbol.getAddress();
Function function = program.getFunctionManager().getFunctionAt(addr);
CodeUnit cu = program.getListing().getCodeUnitAt(function.getEntryPoint());
int transactionID = program.startTransaction("test");
try {
cu.setComment(CodeUnit.PLATE_COMMENT, null);
}
finally {
program.endTransaction(transactionID, true);
}
program.flushEvents();
waitForPostedSwingRunnables();
tx(program, () -> cu.setComment(CodeUnit.PLATE_COMMENT, null));
goToService.goTo(addr);
@ -140,21 +133,17 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
public void testExistingPlateComment() throws Exception {
Symbol symbol = getUniqueSymbol(program, "entry");
Address addr = symbol.getAddress();
int transactionID = program.startTransaction("test");
try {
tx(program, () -> {
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT,
new String[] { "this is", "a plate comment" });
// create a reference to addr
program.getReferenceManager()
.addMemoryReference(getAddr(0x010023ee), addr,
RefType.DATA, SourceType.USER_DEFINED, 0);
}
finally {
program.endTransaction(transactionID, true);
}
program.flushEvents();
waitForPostedSwingRunnables();
ReferenceManager rm = program.getReferenceManager();
rm.addMemoryReference(getAddr(0x010023ee), addr, RefType.DATA, SourceType.USER_DEFINED,
0);
});
cb.updateNow();
Function function = program.getFunctionManager().getFunctionAt(addr);
@ -180,21 +169,16 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
String originalText =
"this is a plate comment that is meant to be longer than the available " +
"width, as to trigger clipping";
int transactionID = program.startTransaction("test");
try {
tx(program, () -> {
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
cu.setCommentAsArray(CodeUnit.PLATE_COMMENT, new String[] { originalText });
// create a reference to addr
program.getReferenceManager()
.addMemoryReference(getAddr(0x010023ee), addr,
RefType.DATA, SourceType.USER_DEFINED, 0);
}
finally {
program.endTransaction(transactionID, true);
}
});
program.flushEvents();
waitForPostedSwingRunnables();
cb.updateNow();
goToService.goTo(addr);
@ -217,17 +201,13 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
Symbol symbol = getUniqueSymbol(program, "entry");
Address addr = symbol.getAddress();
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
int transactionID = program.startTransaction("test");
try {
tx(program, () -> {
CreateFunctionCmd cmd = new CreateFunctionCmd(addr);
cmd.applyTo(program);
cu.setComment(CodeUnit.PLATE_COMMENT, null);
}
finally {
program.endTransaction(transactionID, true);
}
program.flushEvents();
waitForPostedSwingRunnables();
});
cb.updateNow();
goToService.goTo(addr);
@ -263,7 +243,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
new DisassembleCommand(addr, new AddressSet(addr, getAddr(0x01004e39)), true);
tool.execute(cmd, program);
program.flushEvents();
waitForPostedSwingRunnables();
waitForSwing();
setBooleanOption(PlateFieldFactory.SHOW_TRANSITION_PLATES_OPTION, true);
assertTrue(cb.goToField(addr, PlateFieldFactory.FIELD_NAME, 1, 1));
@ -292,7 +272,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
}
@Test
public void testLinesBeforeFunction() throws Exception {
public void testLinesBeforeFunction_WithoutPlateComment() throws Exception {
assertFalse(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
@ -303,6 +283,35 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(2, tf.getNumRows());
}
@Test
public void testLinesBeforeFunction_WithPlateComment() throws Exception {
setBooleanOption(PlateFieldFactory.SHOW_FUNCTION_PLATES_OPTION, true);
setIntOption(PlateFieldFactory.LINES_BEFORE_FUNCTIONS_OPTION, 2);
assertTrue(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
ListingTextField tf = (ListingTextField) cb.getCurrentField();
assertEquals(5, tf.getNumRows());
assertPrecedingBlankLines((PlateListingTextField) tf, 2);
int textRow = 3;
assertCentered((PlateListingTextField) tf, textRow,
PlateFieldFactory.FUNCTION_PLATE_COMMENT);
}
@Test
public void testDefaultPlateCommentGetsCentered_Function() throws Exception {
setBooleanOption(PlateFieldFactory.SHOW_FUNCTION_PLATES_OPTION, true);
assertTrue(cb.goToField(getAddr(0x1001300), PlateFieldFactory.FIELD_NAME, 1, 1));
ListingTextField tf = (ListingTextField) cb.getCurrentField();
assertTrue(tf.getText().indexOf(PlateFieldFactory.FUNCTION_PLATE_COMMENT) >= 0);
assertEquals(3, tf.getNumRows());
int textRow = 1;
assertCentered((PlateListingTextField) tf, textRow,
PlateFieldFactory.FUNCTION_PLATE_COMMENT);
}
@Test
public void testLinesBeforeLabels() throws Exception {
@ -332,7 +341,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
}
@Test
public void testLinesBeforePlates() throws Exception {
public void testLinesBeforePlates_NonDefaultComment() throws Exception {
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(getAddr(0x1001500));
@ -349,20 +358,27 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1));
tf = (ListingTextField) cb.getCurrentField();
assertEquals(5, tf.getNumRows());
assertPrecedingBlankLines((PlateListingTextField) tf, 2);
}
private void createPlateComment(CodeUnit cu, String text) {
int transactionID = program.startTransaction("test");
try {
cu.setComment(CodeUnit.PLATE_COMMENT, text);
}
finally {
program.endTransaction(transactionID, true);
}
program.flushEvents();
waitForPostedSwingRunnables();
cb.updateNow();
@Test
public void testLinesBeforePlates_DefaultPlateComment() throws Exception {
Listing listing = program.getListing();
CodeUnit cu = listing.getCodeUnitAt(getAddr(0x1001500));
setBooleanOption(PlateFieldFactory.SHOW_FUNCTION_PLATES_OPTION, true);
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1));
ListingTextField tf = (ListingTextField) cb.getCurrentField();
assertEquals(3, tf.getNumRows());
setIntOption(PlateFieldFactory.LINES_BEFORE_PLATES_OPTION, 2);
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 1));
tf = (ListingTextField) cb.getCurrentField();
assertEquals(5, tf.getNumRows());
assertPrecedingBlankLines((PlateListingTextField) tf, 2);
}
@Test
@ -419,15 +435,11 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
// add a plate comment that has an address in it
Address addr = getAddr(0x01002911);
int transactionID = program.startTransaction("test");
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
try {
tx(program, () -> {
cu.setComment(CodeUnit.PLATE_COMMENT,
"this is a comment\ngo to the address 0x010028de");
}
finally {
program.endTransaction(transactionID, true);
}
});
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 2, 23));
click(cb, 2);
@ -444,18 +456,14 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
// add a plate comment that has an address in it
Address addr = getAddr(0x01002911);
int transactionID = program.startTransaction("test");
CodeUnit cu = program.getListing().getCodeUnitAt(addr);
try {
tx(program, () -> {
program.getSymbolTable()
.createLabel(addr, testName.getMethodName(),
SourceType.USER_DEFINED);
cu.setComment(CodeUnit.PLATE_COMMENT,
"this is a comment\ngo to the address 0x010028de");
}
finally {
program.endTransaction(transactionID, true);
}
});
int nonCommentHeader = precedingBlankLines + 1; // +1 for the '***' line
int row = nonCommentHeader + 1; // 1 is the second comment row
@ -469,14 +477,9 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testNavigationOnLabel() throws Exception {
// add a plate comment that has "entry" in it
int transactionID = program.startTransaction("test");
CodeUnit cu = program.getListing().getCodeUnitAt(getAddr(0x0100292b));
try {
cu.setComment(CodeUnit.PLATE_COMMENT, "go to entry");
}
finally {
program.endTransaction(transactionID, true);
}
tx(program, () -> cu.setComment(CodeUnit.PLATE_COMMENT, "go to entry"));
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 8));
click(cb, 2);
@ -487,57 +490,31 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testNavigationOnLabelWildcard() throws Exception {
// add a plate comment that has "entry" in it
int transactionID = program.startTransaction("test");
CodeUnit cu = program.getListing().getCodeUnitAt(getAddr(0x01001100));
try {
tx(program, () -> {
cu.setComment(CodeUnit.PLATE_COMMENT, "go to FUN*");
}
finally {
program.endTransaction(transactionID, true);
}
});
assertTrue(cb.goToField(cu.getMinAddress(), PlateFieldFactory.FIELD_NAME, 1, 8));
click(cb, 2);
waitForSwing();
// should get the query results dialog that shows all the default function labels.
List<TableComponentProvider<?>> providers = waitForResultsTable();
List<TableComponentProvider<?>> providers = waitFor(() -> {
List<TableComponentProvider<?>> currentProviders = getNavigationResultsTables();
if (!currentProviders.isEmpty()) {
return currentProviders;
}
return null;
});
assertEquals(1, providers.size());
GhidraProgramTableModel<?> model = waitForModel(providers.get(0));
assertEquals("01001300", model.getValueAt(0, 0).toString());
assertEquals("01001500", model.getValueAt(1, 0).toString());
}
private List<TableComponentProvider<?>> waitForResultsTable() {
List<TableComponentProvider<?>> providers = getNavigationResultsTables();
waitForSwing();
int nWaits = 0;
while (providers.isEmpty() && nWaits < 200) {
sleep(DEFAULT_WAIT_DELAY);
providers = getNavigationResultsTables();
}
if (nWaits >= 200) {
throw new AssertException("Timed out waiting for table model to load");
}
return providers;
}
private List<TableComponentProvider<?>> getNavigationResultsTables() {
TableServicePlugin plugin = getPlugin(tool, TableServicePlugin.class);
List<TableComponentProvider<?>> providers = new ArrayList<>();
runSwing(() -> {
TableComponentProvider<?>[] managedComponents = plugin.getManagedComponents();
for (TableComponentProvider<?> tableComponentProvider : managedComponents) {
providers.add(tableComponentProvider);
}
});
return providers;
}
@Test
public void testClickOnAsterisks() throws Exception {
// click on first row of asterisks in the plate comment
@ -554,6 +531,71 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(tf.getText().startsWith("*****"));
}
private void assertPrecedingBlankLines(PlateListingTextField tf, int n) {
/*
Example: (this may have blank lines before the plate comment)
***************************************************************
* FUNCTION *
***************************************************************
*/
PlateFieldTextField plateTextField = tf.getPlateTextField();
List<String> lines = plateTextField.getLines();
for (int i = 0; i < n; i++) {
String line = lines.get(i);
assertTrue(StringUtils.isBlank(line));
}
}
private void assertCentered(PlateListingTextField tf, int textRow, String commentText) {
/*
Example: (this may have blank lines before the plate comment)
***************************************************************
* FUNCTION *
***************************************************************
*/
PlateFieldTextField plateTextField = tf.getPlateTextField();
List<String> lines = plateTextField.getLines();
String lineText = lines.get(textRow);
lineText = lineText.replaceAll("\\*", "");
int textIndex = lineText.indexOf(commentText);
int textEnd = textIndex + commentText.length();
String pre = lineText.substring(0, textIndex);
String post = lineText.substring(textEnd + 1);
int spacesBefore = StringUtils.countMatches(pre, ' ');
int spacesAfter = StringUtils.countMatches(post, ' ');
int diff = Math.abs(spacesBefore - spacesAfter);
assertTrue(diff < 2);
}
private List<TableComponentProvider<?>> getNavigationResultsTables() {
TableServicePlugin plugin = getPlugin(tool, TableServicePlugin.class);
List<TableComponentProvider<?>> providers = new ArrayList<>();
runSwing(() -> {
TableComponentProvider<?>[] managedComponents = plugin.getManagedComponents();
for (TableComponentProvider<?> tableComponentProvider : managedComponents) {
providers.add(tableComponentProvider);
}
});
return providers;
}
private void createPlateComment(CodeUnit cu, String text) {
tx(program, () -> {
cu.setComment(CodeUnit.PLATE_COMMENT, text);
});
cb.updateNow();
}
private GhidraProgramTableModel<?> waitForModel(TableComponentProvider<?> provider)
throws Exception {
GThreadedTablePanel<?> panel =
@ -570,21 +612,20 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
}
private void setBooleanOption(final String name, final boolean value) throws Exception {
SwingUtilities.invokeAndWait(() -> fieldOptions.setBoolean(name, value));
waitForPostedSwingRunnables();
runSwing(() -> fieldOptions.setBoolean(name, value));
waitForSwing();
cb.updateNow();
}
private void setIntOption(final String name, final int value) throws Exception {
SwingUtilities.invokeAndWait(() -> fieldOptions.setInt(name, value));
waitForPostedSwingRunnables();
runSwing(() -> fieldOptions.setInt(name, value));
waitForSwing();
cb.updateNow();
}
private void resetOptions() {
List<String> names = fieldOptions.getOptionNames();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
for (String name : names) {
if (!name.startsWith("Format Code")) {
continue;
}
@ -595,7 +636,7 @@ public class PlateFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
fieldOptions.setInt(name, 0);
}
}
waitForPostedSwingRunnables();
waitForSwing();
cb.updateNow();
}

View File

@ -143,7 +143,7 @@ public interface Field {
* the given data
* @param row the text row
* @param col the character position
* @return tru if valid
* @return true if valid
*/
public boolean isValid(int row, int col);