diff --git a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java index 46decb1d75..e9173d9378 100644 --- a/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java +++ b/Ghidra/Debug/AnnotationValidator/src/main/java/ghidra/util/database/annotproc/DBAnnotatedFieldValidator.java @@ -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 args = ctx.getArguments(codecType, ctx.DB_FIELD_CODEC_ELEM); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingField.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingField.java index 17046d3472..1e125ca794 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingField.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/ListingField.java @@ -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); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java index adbfeda9cb..ce4194a24c 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/PlateFieldFactory.java @@ -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 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 elements, int commentsStart) { + private boolean addSideBorders(List 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 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 textElements, PlateFieldFactory factory, + PlateFieldTextField(List 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 getLines() { + // open up access for testing + return super.getLines(); + } + int getLeadingFillerLineCount() { int count = 0; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PlateFieldFactoryTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PlateFieldFactoryTest.java index 93a92971ed..4eb454e489 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PlateFieldFactoryTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/util/viewer/field/PlateFieldFactoryTest.java @@ -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> providers = waitForResultsTable(); + List> providers = waitFor(() -> { + + List> 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> waitForResultsTable() { - - List> 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> getNavigationResultsTables() { - TableServicePlugin plugin = getPlugin(tool, TableServicePlugin.class); - - List> 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 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 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> getNavigationResultsTables() { + TableServicePlugin plugin = getPlugin(tool, TableServicePlugin.class); + + List> 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 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(); } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/Field.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/Field.java index 02fa87386d..dd378d7ca5 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/Field.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/fieldpanel/field/Field.java @@ -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);