mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 20:59:58 +00:00
GP-1717 - Fixed Plate Comment rending bugs
This commit is contained in:
parent
c3c20d9e0f
commit
087529c448
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user