mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
GP-1765 - Fixed sometimes incorrect Find Dialog result highlighting
This commit is contained in:
parent
026fad27ab
commit
68b7f88063
7 changed files with 534 additions and 188 deletions
|
@ -20,7 +20,6 @@ import static org.junit.Assert.*;
|
|||
import java.awt.Component;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
|
@ -55,12 +54,10 @@ import ghidra.program.model.listing.*;
|
|||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.test.*;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private static final int DIALOG_WAIT_TIME = 3000;
|
||||
|
||||
private TestEnv env;
|
||||
private PluginTool tool;
|
||||
private AddressFactory addrFactory;
|
||||
|
@ -104,13 +101,13 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
ActionContext actionContext = cb.getProvider().getActionContext(null);
|
||||
assertNull(actionContext);
|
||||
actionContext = new ActionContext();
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(createThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(editThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
env.showTool();
|
||||
loadProgram("notepad");
|
||||
|
||||
|
@ -119,21 +116,21 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(editThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
assertTrue(cb.goToField(addr("0x1001000"), "Address", 0, 0));
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(createThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(editThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
assertTrue(cb.goToField(addr("0x1006420"), "Address", 0, 0));
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
|
@ -146,46 +143,46 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
assertTrue(cb.goToField(addr("0x1006420"), "Function Signature", 0, 0));
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!createThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
assertTrue(cb.goToField(addr("0x1006420"), "Variable Name", 1, 0, 0));
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!createThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
createThunk(addr("0x10030d2"), "comdlg32.dll::CommDlgExtendedError", true);
|
||||
|
||||
assertTrue(cb.goToField(addr("0x10030d2"), "Function Signature", 0, 0));
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!createThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(createThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
closeProgram();
|
||||
actionContext = cb.getProvider().getActionContext(null);
|
||||
assertNull(actionContext);
|
||||
actionContext = new ActionContext();
|
||||
assertTrue(!createFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteFunction.isEnabledForContext(actionContext));
|
||||
assertTrue(!editThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!revertThunk.isEnabledForContext(actionContext));
|
||||
assertTrue(!editComment.isEnabledForContext(actionContext));
|
||||
assertTrue(!deleteComment.isEnabledForContext(actionContext));
|
||||
assertFalse(createFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteFunction.isEnabledForContext(actionContext));
|
||||
assertFalse(editThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(revertThunk.isEnabledForContext(actionContext));
|
||||
assertFalse(editComment.isEnabledForContext(actionContext));
|
||||
assertFalse(deleteComment.isEnabledForContext(actionContext));
|
||||
|
||||
}
|
||||
|
||||
|
@ -323,7 +320,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
performAction(editThunk, cb.getProvider(), false);
|
||||
|
||||
ThunkReferenceAddressDialog thunkDlg =
|
||||
waitForDialogComponent(null, ThunkReferenceAddressDialog.class, 100);
|
||||
waitForDialogComponent(ThunkReferenceAddressDialog.class);
|
||||
assertNotNull(thunkDlg);
|
||||
JTextField thunkedEntryField = findComponent(thunkDlg, JTextField.class);
|
||||
assertEquals("comdlg32.dll::CommDlgExtendedError", thunkedEntryField.getText());
|
||||
|
@ -374,13 +371,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(func.isThunk());
|
||||
assertEquals("CommDlgExtendedError", func.getName());
|
||||
|
||||
int txId = program.startTransaction("Set Name");
|
||||
try {
|
||||
func.setName("foo", SourceType.USER_DEFINED);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
tx(program, () -> func.setName("foo", SourceType.USER_DEFINED));
|
||||
|
||||
performAction(revertThunk, cb.getProvider(), false);
|
||||
waitForBusyTool();
|
||||
|
@ -406,7 +397,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
Function func = program.getListing().getFunctionAt(addr("0x1006420"));
|
||||
assertNotNull(func);
|
||||
|
||||
assertTrue(!func.isThunk());
|
||||
assertFalse(func.isThunk());
|
||||
assertEquals("entry", func.getName());
|
||||
assertTrue(func.getLocalVariables().length != 0);
|
||||
|
||||
|
@ -417,7 +408,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
waitForSwing();
|
||||
|
||||
ThunkReferenceAddressDialog thunkDlg =
|
||||
waitForDialogComponent(null, ThunkReferenceAddressDialog.class, 100);
|
||||
waitForDialogComponent(ThunkReferenceAddressDialog.class);
|
||||
assertNotNull(thunkDlg);
|
||||
JTextField thunkedEntryField = findComponent(thunkDlg, JTextField.class);
|
||||
assertEquals("", thunkedEntryField.getText());
|
||||
|
@ -433,7 +424,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
undo(program);// undo changed function
|
||||
|
||||
assertTrue(!func.isThunk());
|
||||
assertFalse(func.isThunk());
|
||||
assertEquals("entry", func.getName());
|
||||
assertTrue(func.getLocalVariables().length != 0);
|
||||
|
||||
|
@ -530,11 +521,10 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
loadProgram("notepad");
|
||||
createFunctionAtEntry();
|
||||
assertTrue(cb.goToField(addr("0x1006420"), "Variable Name", 1, 0, 0));
|
||||
assertTrue(!deleteComment.isEnabledForContext(cb.getProvider().getActionContext(null)));
|
||||
assertFalse(deleteComment.isEnabledForContext(cb.getProvider().getActionContext(null)));
|
||||
performAction(editComment, cb.getProvider(), false);
|
||||
waitForBusyTool();
|
||||
VariableCommentDialog vcd = waitForDialogComponent(tool.getToolFrame(),
|
||||
VariableCommentDialog.class, DIALOG_WAIT_TIME);
|
||||
VariableCommentDialog vcd = waitForDialogComponent(VariableCommentDialog.class);
|
||||
assertNotNull(vcd);
|
||||
JTextArea textArea = findComponent(vcd, JTextArea.class);
|
||||
triggerText(textArea, "My New Comment");
|
||||
|
@ -544,8 +534,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(deleteComment.isEnabledForContext(cb.getProvider().getActionContext(null)));
|
||||
|
||||
performAction(editComment, cb.getProvider(), false);
|
||||
vcd = waitForDialogComponent(tool.getToolFrame(), VariableCommentDialog.class,
|
||||
DIALOG_WAIT_TIME);
|
||||
vcd = waitForDialogComponent(VariableCommentDialog.class);
|
||||
textArea = findComponent(vcd, JTextArea.class);
|
||||
triggerText(textArea, "more stuff");
|
||||
pressButtonByText(vcd.getComponent(), "OK");
|
||||
|
@ -894,17 +883,13 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
// put a byte at local_8
|
||||
Function function = program.getListing().getFunctionAt(a);
|
||||
Variable[] vars = function.getLocalVariables(VariableFilter.STACK_VARIABLE_FILTER);
|
||||
int transactionID = program.startTransaction("test");
|
||||
try {
|
||||
DataType byteDT = program.getDataTypeManager().addDataType(new ByteDataType(),
|
||||
tx(program, () -> {
|
||||
DataType byteDT = program.getDataTypeManager()
|
||||
.addDataType(new ByteDataType(),
|
||||
DataTypeConflictHandler.DEFAULT_HANDLER);
|
||||
vars[1].setDataType(byteDT, SourceType.ANALYSIS);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(transactionID, true);
|
||||
}
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
});
|
||||
|
||||
cb.updateNow();
|
||||
|
||||
assertTrue(cb.goToField(a, "Variable Type", 5, 0, 0));
|
||||
|
@ -912,18 +897,14 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
performAction(createArray, cb.getProvider(), false);
|
||||
waitForSwing();
|
||||
final NumberInputDialog d =
|
||||
env.waitForDialogComponent(NumberInputDialog.class, DIALOG_WAIT_TIME);
|
||||
NumberInputDialog d = waitForDialogComponent(NumberInputDialog.class);
|
||||
|
||||
assertNotNull(d);
|
||||
vars = function.getLocalVariables(VariableFilter.STACK_VARIABLE_FILTER);
|
||||
|
||||
assertEquals(1, d.getMin());
|
||||
assertEquals(Integer.MAX_VALUE, d.getMax());
|
||||
|
||||
final AtomicInteger result = new AtomicInteger(0);
|
||||
runSwing(() -> result.set(d.getValue()));
|
||||
assertEquals(12, result.get());
|
||||
int result = runSwing(() -> d.getValue());
|
||||
assertEquals(12, result);
|
||||
|
||||
runSwing(() -> d.setInput(4));
|
||||
|
||||
|
@ -1032,7 +1013,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
performAction(rename, cb.getProvider(), false);
|
||||
waitForBusyTool();
|
||||
|
||||
AddEditDialog dialog = env.waitForDialogComponent(AddEditDialog.class, DIALOG_WAIT_TIME);
|
||||
AddEditDialog dialog = waitForDialogComponent(AddEditDialog.class);
|
||||
assertNotNull(dialog);
|
||||
|
||||
GhidraComboBox<?> combo = findComponent(dialog, GhidraComboBox.class);
|
||||
|
@ -1058,14 +1039,14 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
performAction(rename, cb.getProvider(), false);
|
||||
|
||||
dialog = env.waitForDialogComponent(AddEditDialog.class, DIALOG_WAIT_TIME);
|
||||
dialog = waitForDialogComponent(AddEditDialog.class);
|
||||
assertNotNull(dialog);
|
||||
|
||||
JComboBox<?> nameBox = (JComboBox<?>) getInstanceField("labelNameChoices", dialog);
|
||||
final JTextField editorField = (JTextField) nameBox.getEditor().getEditorComponent();
|
||||
JTextField editorField = (JTextField) nameBox.getEditor().getEditorComponent();
|
||||
assertNotNull(editorField);
|
||||
SwingUtilities.invokeAndWait(() -> editorField.setText("fred"));
|
||||
//typeText("fred");
|
||||
runSwing(() -> editorField.setText("fred"));
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
|
||||
assertEquals("fred", cb.getCurrentFieldText());
|
||||
|
@ -1084,17 +1065,14 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals("dword ptr [EBP + fred],0x0", cb.getCurrentFieldText());
|
||||
performAction(renameFunctionVar, cb.getProvider(), false);
|
||||
|
||||
dialog = env.waitForDialogComponent(AddEditDialog.class, DIALOG_WAIT_TIME);
|
||||
dialog = waitForDialogComponent(AddEditDialog.class);
|
||||
assertNotNull(dialog);
|
||||
|
||||
nameBox = (JComboBox<?>) getInstanceField("labelNameChoices", dialog);
|
||||
final JTextField editorField2 = (JTextField) nameBox.getEditor().getEditorComponent();
|
||||
JTextField editorField2 = (JTextField) nameBox.getEditor().getEditorComponent();
|
||||
assertNotNull(editorField);
|
||||
SwingUtilities.invokeAndWait(() -> editorField2.setText("bob"));
|
||||
runSwing(() -> editorField2.setText("bob"));
|
||||
|
||||
//
|
||||
// typeText("bob");
|
||||
// waitForSwing();
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForBusyTool();
|
||||
assertEquals("dword ptr [EBP + bob],0x0", cb.getCurrentFieldText());
|
||||
|
@ -1137,7 +1115,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
undo(program);
|
||||
cb.updateNow();
|
||||
assertTrue(!cb.getCurrentFieldText().equals("undefined"));
|
||||
assertFalse(cb.getCurrentFieldText().equals("undefined"));
|
||||
redo(program);
|
||||
cb.updateNow();
|
||||
assertEquals("undefined", cb.getCurrentFieldText());
|
||||
|
@ -1235,7 +1213,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
performAction(rename, cb.getProvider(), false);
|
||||
waitForBusyTool();
|
||||
|
||||
AddEditDialog dialog = env.waitForDialogComponent(AddEditDialog.class, DIALOG_WAIT_TIME);
|
||||
AddEditDialog dialog = waitForDialogComponent(AddEditDialog.class);
|
||||
assertNotNull(dialog);
|
||||
|
||||
GhidraComboBox<?> combo = findComponent(dialog, GhidraComboBox.class);
|
||||
|
@ -1246,7 +1224,6 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
waitForBusyTool();
|
||||
|
||||
assertEquals("hello", cb.getCurrentFieldText());
|
||||
|
||||
assertEquals("hello", function.getName());
|
||||
}
|
||||
|
||||
|
@ -1262,18 +1239,18 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
Function entry = getFunction("entry");
|
||||
assertNotNull(entry);
|
||||
|
||||
Set<Function> called = entry.getCalledFunctions(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
Set<Function> called = entry.getCalledFunctions(TaskMonitor.DUMMY);
|
||||
assertEquals(4, called.size());
|
||||
Set<Function> calling = entry.getCallingFunctions(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
Set<Function> calling = entry.getCallingFunctions(TaskMonitor.DUMMY);
|
||||
assertEquals(0, calling.size());// nobody calls entry
|
||||
|
||||
for (Function f : called) {
|
||||
Set<Function> calling_f = f.getCallingFunctions(TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
Set<Function> calling_f = f.getCallingFunctions(TaskMonitor.DUMMY);
|
||||
assertTrue(calling_f.contains(entry));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Tests that setting a function register param to have a data type larger than
|
||||
* its storage allows will produce an error message in the status box, and not simply fail
|
||||
* silently.
|
||||
|
@ -1292,8 +1269,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(cb.goToField(addr("0x1006420"), "Variable Type", 0, 0));
|
||||
|
||||
performAction(chooseDataType, cb.getProvider(), false);
|
||||
DataTypeSelectionDialog dialog =
|
||||
waitForDialogComponent(null, DataTypeSelectionDialog.class, DIALOG_WAIT_TIME);
|
||||
DataTypeSelectionDialog dialog = waitForDialogComponent(DataTypeSelectionDialog.class);
|
||||
|
||||
setEditorText(dialog, "int[0x8888888]");
|
||||
|
||||
|
@ -1403,13 +1379,12 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
throws Exception {
|
||||
|
||||
deleteExistingFunction(thunkEntry);
|
||||
|
||||
assertTrue(cb.goToField(thunkEntry, "Address", 0, 0));
|
||||
|
||||
performAction(createThunk, cb.getProvider(), false);
|
||||
|
||||
ThunkReferenceAddressDialog thunkDialog =
|
||||
waitForDialogComponent(null, ThunkReferenceAddressDialog.class, 100);
|
||||
waitForDialogComponent(ThunkReferenceAddressDialog.class);
|
||||
assertNotNull(thunkDialog);
|
||||
JTextField thunkedEntryField = findComponent(thunkDialog, JTextField.class);
|
||||
assertEquals(expectedDefault ? refFunc : "", thunkedEntryField.getText());
|
||||
|
@ -1486,12 +1461,6 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
performAction(createFunction, cb.getProvider(), false);
|
||||
|
||||
// FunctionNameDialog d = (FunctionNameDialog)waitForDialogComponent(tool.getToolFrame(),
|
||||
// FunctionNameDialog.class, 2000);
|
||||
//assertNotNull(d);
|
||||
//pressButtonByText(d, "OK");
|
||||
|
||||
waitForBusyTool();
|
||||
|
||||
// cheat setting custom storage since we are not testing the edit function dialog here
|
||||
|
@ -1510,13 +1479,7 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void setCustomParameterStorage(Function function, boolean enabled) {
|
||||
int txId = program.startTransaction("Set Custom Storage");
|
||||
try {
|
||||
function.setCustomVariableStorage(enabled);
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
tx(program, () -> function.setCustomVariableStorage(enabled));
|
||||
}
|
||||
|
||||
private void waitForBusyTool() {
|
||||
|
@ -1569,20 +1532,13 @@ public class Function1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private void loadProgram(String programName) throws Exception {
|
||||
|
||||
if ("notepad".equals(programName)) {
|
||||
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
|
||||
program = builder.getProgram();
|
||||
|
||||
ProgramManager pm = tool.getService(ProgramManager.class);
|
||||
pm.openProgram(program.getDomainFile());
|
||||
builder.dispose();
|
||||
waitForSwing();
|
||||
addrFactory = program.getAddressFactory();
|
||||
}
|
||||
else {
|
||||
Assert.fail("don't have program: " + programName);
|
||||
}
|
||||
}
|
||||
|
||||
private Function getFunction(String name) {
|
||||
List<Function> functions = program.getListing().getGlobalFunctions(name);
|
||||
|
|
|
@ -122,27 +122,27 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
@Override
|
||||
public void modelSizeChanged(IndexMapper mapper) {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
listeners.get(i).modelSizeChanged(mapper);
|
||||
for (LayoutModelListener listener : listeners) {
|
||||
listener.modelSizeChanged(mapper);
|
||||
}
|
||||
}
|
||||
|
||||
public void modelChanged() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
listeners.get(i).modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||
for (LayoutModelListener listener : listeners) {
|
||||
listener.modelSizeChanged(IndexMapper.IDENTITY_MAPPER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged(BigInteger start, BigInteger end) {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
listeners.get(i).dataChanged(start, end);
|
||||
for (LayoutModelListener listener : listeners) {
|
||||
listener.dataChanged(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
public void layoutChanged() {
|
||||
for (int i = 0; i < listeners.size(); ++i) {
|
||||
listeners.get(i).dataChanged(BigInteger.ZERO, numIndexes);
|
||||
for (LayoutModelListener listener : listeners) {
|
||||
listener.dataChanged(BigInteger.ZERO, numIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,11 +336,11 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
for (String element : errlines_init) {
|
||||
splitToMaxWidthLines(errlines, element);
|
||||
}
|
||||
for (int i = 0; i < errlines.size(); ++i) {
|
||||
for (String errline : errlines) {
|
||||
ClangTokenGroup line = new ClangTokenGroup(docroot);
|
||||
ClangBreak lineBreak = new ClangBreak(line, 1);
|
||||
ClangSyntaxToken message =
|
||||
new ClangSyntaxToken(line, errlines.get(i), ClangXML.COMMENT_COLOR);
|
||||
new ClangSyntaxToken(line, errline, ClangXML.COMMENT_COLOR);
|
||||
line.AddTokenGroup(lineBreak);
|
||||
line.AddTokenGroup(message);
|
||||
docroot.AddTokenGroup(line);
|
||||
|
@ -378,14 +378,23 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
int row = currentLocation.getIndex().intValue();
|
||||
for (int i = row; i < fieldList.length; i++) {
|
||||
ClangTextField field = (ClangTextField) fieldList[i];
|
||||
String textLine =
|
||||
String partialLine =
|
||||
getTextLineFromOffset((i == row) ? currentLocation : null, field, true);
|
||||
|
||||
SearchMatch match = matcher.apply(textLine);
|
||||
if (match != SearchMatch.NO_MATCH) {
|
||||
if (i == row) {
|
||||
SearchMatch match = matcher.apply(partialLine);
|
||||
if (match == SearchMatch.NO_MATCH) {
|
||||
continue;
|
||||
}
|
||||
if (i == row) { // cursor is on this line
|
||||
//
|
||||
// The match start for all lines without the cursor will be relative to the start
|
||||
// of the line, which is 0. However, when searching on the row with the cursor,
|
||||
// the match start is relative to the cursor position. Update the start to
|
||||
// compensate for the difference between the start of the line and the cursor.
|
||||
//
|
||||
String fullLine = field.getText();
|
||||
match.start += fullLine.length() - textLine.length();
|
||||
int cursorOffset = fullLine.length() - partialLine.length();
|
||||
match.start += cursorOffset;
|
||||
match.end += cursorOffset;
|
||||
}
|
||||
FieldNumberColumnPair pair = getFieldIndexFromOffset(match.start, field);
|
||||
FieldLocation fieldLocation =
|
||||
|
@ -394,7 +403,6 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
return new FieldBasedSearchLocation(fieldLocation, match.start, match.end - 1,
|
||||
searchString, true);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -442,7 +450,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
if (matcher.find()) {
|
||||
int start = matcher.start();
|
||||
int end = matcher.end();
|
||||
return new SearchMatch(start, end);
|
||||
return new SearchMatch(start, end, textLine);
|
||||
}
|
||||
|
||||
return SearchMatch.NO_MATCH;
|
||||
|
@ -469,7 +477,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
end = matcher.end();
|
||||
}
|
||||
|
||||
return new SearchMatch(start, end);
|
||||
return new SearchMatch(start, end, textLine);
|
||||
};
|
||||
|
||||
return findNextTokenGoingBackward(reverse, searchString, currentLocation);
|
||||
|
@ -486,7 +494,8 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
if (index == -1) {
|
||||
return SearchMatch.NO_MATCH;
|
||||
}
|
||||
return new SearchMatch(index, index + searchString.length());
|
||||
|
||||
return new SearchMatch(index, index + searchString.length(), textLine);
|
||||
};
|
||||
|
||||
return findNextTokenGoingForward(function, searchString, currentLocation);
|
||||
|
@ -498,7 +507,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
if (index == -1) {
|
||||
return SearchMatch.NO_MATCH;
|
||||
}
|
||||
return new SearchMatch(index, index + searchString.length());
|
||||
return new SearchMatch(index, index + searchString.length(), textLine);
|
||||
};
|
||||
|
||||
return findNextTokenGoingBackward(function, searchString, currentLocation);
|
||||
|
@ -506,6 +515,7 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
private String getTextLineFromOffset(FieldLocation location, ClangTextField textField,
|
||||
boolean forwardSearch) {
|
||||
|
||||
if (location == null) { // the cursor location is not on this line; use all of the text
|
||||
return textField.getText();
|
||||
}
|
||||
|
@ -518,12 +528,16 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
|
||||
if (forwardSearch) {
|
||||
|
||||
// Protects against the location column being out of range (this can
|
||||
// happen if we're searching forward and the cursor is past the last token).
|
||||
if (location.getCol() + 1 >= partialText.length()) {
|
||||
int nextCol = location.getCol();
|
||||
|
||||
// protects against the location column being out of range (this can happen if we're
|
||||
// searching forward and the cursor is past the last token)
|
||||
if (nextCol >= partialText.length()) {
|
||||
return "";
|
||||
}
|
||||
return partialText.substring(location.getCol() + 1);
|
||||
|
||||
// skip a character to start the next search; this prevents matching the previous match
|
||||
return partialText.substring(nextCol);
|
||||
}
|
||||
|
||||
// backwards search
|
||||
|
@ -539,13 +553,23 @@ public class ClangLayoutController implements LayoutModel, LayoutModelListener {
|
|||
}
|
||||
|
||||
private static class SearchMatch {
|
||||
private static SearchMatch NO_MATCH = new SearchMatch(-1, -1);
|
||||
private static SearchMatch NO_MATCH = new SearchMatch(-1, -1, null);
|
||||
private int start;
|
||||
private int end;
|
||||
private String textLine;
|
||||
|
||||
SearchMatch(int start, int end) {
|
||||
SearchMatch(int start, int end, String textLine) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.textLine = textLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == NO_MATCH) {
|
||||
return "NO MATCH";
|
||||
}
|
||||
return "[start=" + start + ",end=" + end + "]: " + textLine;
|
||||
}
|
||||
}
|
||||
//==================================================================================================
|
||||
|
|
|
@ -838,6 +838,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
|||
repaint();
|
||||
}
|
||||
|
||||
public FieldBasedSearchLocation getSearchResults() {
|
||||
return (FieldBasedSearchLocation) currentSearchLocation;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// End Search Methods
|
||||
//==================================================================================================
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.decompile.actions;
|
|||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -58,6 +59,31 @@ public class FindAction extends AbstractDecompilerAction {
|
|||
return findDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
FindDialog dialog = getFindDialog(decompilerPanel);
|
||||
String text = decompilerPanel.getSelectedText();
|
||||
if (text == null) {
|
||||
text = decompilerPanel.getHighlightedText();
|
||||
|
||||
// note: if we decide to grab the text under the cursor, then use
|
||||
// text = decompilerPanel.getTextUnderCursor();
|
||||
}
|
||||
|
||||
if (!StringUtils.isBlank(text)) {
|
||||
dialog.setSearchText(text);
|
||||
}
|
||||
|
||||
// show over the root frame, so the user can still see the Decompiler window
|
||||
context.getTool().showDialog(dialog);
|
||||
}
|
||||
|
||||
private static class DecompilerSearcher implements FindDialogSearcher {
|
||||
|
||||
private DecompilerPanel decompilerPanel;
|
||||
|
@ -112,35 +138,60 @@ public class FindAction extends AbstractDecompilerAction {
|
|||
public SearchLocation search(String text, CursorPosition position, boolean searchForward,
|
||||
boolean useRegex) {
|
||||
DecompilerCursorPosition decompilerCursorPosition = (DecompilerCursorPosition) position;
|
||||
FieldLocation fieldLocation = decompilerCursorPosition.getFieldLocation();
|
||||
return useRegex ? decompilerPanel.searchTextRegex(text, fieldLocation, searchForward)
|
||||
: decompilerPanel.searchText(text, fieldLocation, searchForward);
|
||||
FieldLocation startLocation =
|
||||
getNextSearchStartLocation(decompilerCursorPosition, searchForward);
|
||||
return useRegex ? decompilerPanel.searchTextRegex(text, startLocation, searchForward)
|
||||
: decompilerPanel.searchText(text, startLocation, searchForward);
|
||||
}
|
||||
|
||||
private FieldLocation getNextSearchStartLocation(
|
||||
DecompilerCursorPosition decompilerCursorPosition, boolean searchForward) {
|
||||
|
||||
FieldLocation startLocation = decompilerCursorPosition.getFieldLocation();
|
||||
FieldBasedSearchLocation currentSearchLocation = decompilerPanel.getSearchResults();
|
||||
if (currentSearchLocation == null) {
|
||||
return startLocation; // nothing to do; no prior search hit
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
|
||||
return true;
|
||||
//
|
||||
// Special Case Handling: Start the search at the cursor location by default.
|
||||
// However, if the cursor location is at the beginning of previous search hit, then
|
||||
// move the cursor forward by one character to ensure the previous search hit is not
|
||||
// found.
|
||||
//
|
||||
// Note: for a forward or backward search the cursor is placed at the beginning of the
|
||||
// match.
|
||||
//
|
||||
if (Objects.equals(startLocation, currentSearchLocation.getFieldLocation())) {
|
||||
|
||||
if (searchForward) {
|
||||
// Given:
|
||||
// -search text: 'fox'
|
||||
// -search domain: 'What the |fox say'
|
||||
// -a previous search hit just before 'fox'
|
||||
//
|
||||
// Move the cursor just past the 'f' so the next forward search will not
|
||||
// find the current 'fox' hit. Thus the new search domain for this line
|
||||
// will be: "ox say"
|
||||
//
|
||||
startLocation.col += 1;
|
||||
}
|
||||
else {
|
||||
// Given:
|
||||
// -search text: 'fox'
|
||||
// -search domain: 'What the |fox say'
|
||||
// -a previous search hit just before 'fox'
|
||||
//
|
||||
// Move the cursor just past the 'o' so the next backward search will not
|
||||
// find the current 'fox' hit. Thus the new search domain for this line
|
||||
// will be: "What the fo"
|
||||
//
|
||||
int length = currentSearchLocation.getMatchLength();
|
||||
startLocation.col += length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decompilerActionPerformed(DecompilerActionContext context) {
|
||||
DecompilerPanel decompilerPanel = context.getDecompilerPanel();
|
||||
FindDialog dialog = getFindDialog(decompilerPanel);
|
||||
String text = decompilerPanel.getSelectedText();
|
||||
if (text == null) {
|
||||
text = decompilerPanel.getHighlightedText();
|
||||
|
||||
// note: if we decide to grab the text under the cursor, then use
|
||||
// text = decompilerPanel.getTextUnderCursor();
|
||||
return startLocation;
|
||||
}
|
||||
|
||||
if (!StringUtils.isBlank(text)) {
|
||||
dialog.setSearchText(text);
|
||||
}
|
||||
|
||||
// show over the root frame, so the user can still see the Decompiler window
|
||||
context.getTool().showDialog(dialog);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.FindDialog;
|
||||
import docking.widgets.dialogs.InputDialog;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
|
||||
public class DecompilerFindDialogTest extends AbstractDecompilerTest {
|
||||
|
||||
private FindDialog findDialog;
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
close(findDialog);
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Program getProgram() throws Exception {
|
||||
return buildProgram();
|
||||
}
|
||||
|
||||
private Program buildProgram() throws Exception {
|
||||
ClassicSampleX86ProgramBuilder builder =
|
||||
new ClassicSampleX86ProgramBuilder("notepad", false, this);
|
||||
return builder.getProgram();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFind() {
|
||||
|
||||
/*
|
||||
|
||||
bool FUN_01002239(int param_1)
|
||||
|
||||
{
|
||||
undefined4 uVar1;
|
||||
int iVar2;
|
||||
undefined4 *puVar3;
|
||||
bool bVar4;
|
||||
undefined *puVar5;
|
||||
undefined2 local_210;
|
||||
undefined4 local_20e [129];
|
||||
int local_8;
|
||||
|
||||
local_210 = 0;
|
||||
puVar3 = local_20e;
|
||||
...
|
||||
...
|
||||
...
|
||||
*/
|
||||
|
||||
decompile("1002239");
|
||||
|
||||
String text = "puVar";
|
||||
showFind(text);
|
||||
next();
|
||||
|
||||
int length = text.length();
|
||||
int line = 9;
|
||||
int column = 12;
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
line = 11;
|
||||
column = 11;
|
||||
next();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
line = 17;
|
||||
column = 0;
|
||||
next();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
line = 11;
|
||||
column = 11;
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
line = 9;
|
||||
column = 12;
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithinField() {
|
||||
|
||||
//
|
||||
// Test that we find multiple search hits within a field in both directions
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
bool FUN_01002239(int param_1)
|
||||
|
||||
{
|
||||
undefined4 uVar1;
|
||||
int iVar2;
|
||||
undefined4 *pu1111Var;
|
||||
bool bVar4;
|
||||
undefined *puVar5;
|
||||
undefined2 local_210;
|
||||
undefined4 local_20e [129];
|
||||
int local_8;
|
||||
|
||||
local_210 = 0;
|
||||
puVar3 = local_20e;
|
||||
...
|
||||
...
|
||||
...
|
||||
*/
|
||||
|
||||
decompile("1002239");
|
||||
|
||||
int line = 9;
|
||||
int column = 12;
|
||||
setDecompilerLocation(line, column);
|
||||
|
||||
rename("pu1111Var");
|
||||
|
||||
// reset
|
||||
setDecompilerLocation(1, 0);
|
||||
|
||||
String text = "11";
|
||||
showFind(text);
|
||||
next();
|
||||
|
||||
int length = 2;
|
||||
line = 9;
|
||||
column = 14;
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
column++; // same variable; one char over
|
||||
next();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
column++; // same variable; one char over
|
||||
next();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
column--; // same variable; one char over
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
column--; // same variable; one char over
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleSearchHitsOnTheSameLine() {
|
||||
|
||||
//
|
||||
// Test that we find multiple search hits within a single line
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
bool FUN_01002239(int param_1)
|
||||
|
||||
{
|
||||
undefined4 uVar1;
|
||||
int iVar2;
|
||||
undefined4 *puVar3;
|
||||
bool bVar4;
|
||||
undefined *puVar5;
|
||||
undefined2 local_210;
|
||||
undefined4 local_20e [129];
|
||||
int local_8;
|
||||
|
||||
local_210 = 0;
|
||||
puVar3 = local_20e;
|
||||
|
||||
local_210 = 0;
|
||||
puVar3 = local_20e;
|
||||
for (iVar2 = 0x81; iVar2 != 0; iVar2 = iVar2 + -1) {
|
||||
*puVar3 = 0;
|
||||
puVar3 = puVar3 + 1;
|
||||
}
|
||||
|
||||
...
|
||||
...
|
||||
...
|
||||
*/
|
||||
|
||||
decompile("1002239");
|
||||
|
||||
// skip past some search hits for test brevity
|
||||
setDecompilerLocation(18, 0);
|
||||
|
||||
String text = "puVar3";
|
||||
showFind(text);
|
||||
next();
|
||||
|
||||
int length = text.length();
|
||||
int line = 19;
|
||||
int column = 1;
|
||||
assertSearchHit(line, column, length); // *|puVar3 = 0;
|
||||
|
||||
line = 20;
|
||||
column = 0;
|
||||
next();
|
||||
assertSearchHit(line, column, length); // |puVar3 = puVar3 + 1;
|
||||
|
||||
line = 20;
|
||||
column = 9;
|
||||
next();
|
||||
assertSearchHit(line, column, length); // puVar3 = |puVar3 + 1;
|
||||
|
||||
line = 20;
|
||||
column = 0;
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
|
||||
line = 19;
|
||||
column = 1;
|
||||
previous();
|
||||
assertSearchHit(line, column, length);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void next() {
|
||||
runSwing(() -> findDialog.next());
|
||||
}
|
||||
|
||||
private void previous() {
|
||||
runSwing(() -> findDialog.previous());
|
||||
}
|
||||
|
||||
private void assertSearchHit(int line, int column, int length) {
|
||||
|
||||
waitForSwing();
|
||||
assertCurrentLocation(line, column);
|
||||
|
||||
DecompilerPanel panel = getDecompilerPanel();
|
||||
FieldBasedSearchLocation searchResults = panel.getSearchResults();
|
||||
FieldLocation searchCursorLocation = searchResults.getFieldLocation();
|
||||
int searchLineNumber = searchCursorLocation.getIndex().intValue() + 1;
|
||||
assertEquals("Search result is on the wrong line", line, searchLineNumber);
|
||||
|
||||
int searchStartColumn = searchResults.getStartIndexInclusive();
|
||||
assertEquals("Search result does not start on the correct character", column,
|
||||
searchStartColumn);
|
||||
|
||||
int searchEndColumn = searchResults.getEndIndexInclusive();
|
||||
assertEquals("Search result does not end on the correct character", column + length - 1,
|
||||
searchEndColumn);
|
||||
}
|
||||
|
||||
private void assertCurrentLocation(int line, int col) {
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
FieldLocation actual = panel.getCursorPosition();
|
||||
FieldLocation expected = loc(line, col);
|
||||
assertEquals("Decompiler cursor is not at the expected location", expected, actual);
|
||||
}
|
||||
|
||||
private void showFind(String text) {
|
||||
DockingActionIf findAction = getAction(decompiler, "Find");
|
||||
performAction(findAction, provider, true);
|
||||
findDialog = waitForDialogComponent(FindDialog.class);
|
||||
runSwing(() -> findDialog.setSearchText(text));
|
||||
}
|
||||
|
||||
private void rename(String newName) {
|
||||
DockingActionIf action = getAction(decompiler, "Rename Variable");
|
||||
performAction(action, provider.getActionContext(null), false);
|
||||
|
||||
InputDialog dialog = waitForDialogComponent(InputDialog.class);
|
||||
runSwing(() -> dialog.setValue(newName));
|
||||
|
||||
pressButtonByText(dialog, "OK");
|
||||
waitForDecompiler();
|
||||
}
|
||||
|
||||
}
|
|
@ -136,6 +136,14 @@ public class FindDialog extends DialogComponentProvider {
|
|||
textField.setText("");
|
||||
}
|
||||
|
||||
public void next() {
|
||||
doSearch(true);
|
||||
}
|
||||
|
||||
public void previous() {
|
||||
doSearch(false);
|
||||
}
|
||||
|
||||
private void doSearch(boolean forward) {
|
||||
|
||||
if (!nextButton.isEnabled()) {
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class FindDialogTest {
|
||||
|
||||
|
@ -32,8 +32,8 @@ public class FindDialogTest {
|
|||
findDialog.setHistory(List.of("search1"));
|
||||
|
||||
String searchText = "search"; // a prefix of an existing history entry
|
||||
findDialog.setSearchText(searchText);
|
||||
assertEquals(searchText, AbstractGenericTest.runSwing(() -> findDialog.getSearchText()));
|
||||
Swing.runNow(() -> findDialog.setSearchText(searchText));
|
||||
assertEquals(searchText, Swing.runNow(() -> findDialog.getSearchText()));
|
||||
}
|
||||
|
||||
private class DummySearcher implements FindDialogSearcher {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue