Merge remote-tracking branch

'origin/GP-5326_ghidragon_adding_address_and_data_options'
(Closes #7407)
This commit is contained in:
Ryan Kurtz
2025-04-07 14:52:56 -04:00
14 changed files with 909 additions and 125 deletions
@@ -972,19 +972,21 @@
<OL>
<LI>Right mouse click on a structure member in the Listing</LI>
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Edit Field</B>
<LI>Choose the <B>Data</B><IMG src="help/shared/arrow.gif"> <B>Quick Edit Field</B>
action to bring the up the <A href=
"#Edit_Field_Dialog">Edit Field Dialog</A> </LI>
</OL>
<P>The second way is more useful for changing the names of multiple members:</P>
<P>The second way is more useful for changing the names of multiple members. This method
will show the full Structure Editor:
</P>
<OL>
<LI>Place the cursor on the first line of the structure</LI>
<LI>Press mouse-right over the structure and choose <B>Data</B><IMG src=
"help/shared/arrow.gif"> <B>Edit Data Type...</B></LI>
"help/shared/arrow.gif"> <B>Edit Data Type</B></LI>
<LI>Edit the field name for the structure member</LI>
</OL>
@@ -1369,13 +1371,17 @@
bringing up the entire structure or union editor. To edit a field, click anywhere on the line
displaying that field in the listing and then right click and select <B>Data</B><IMG
src="help/shared/arrow.gif"> <B>Edit Field</B> from the popup context menu.</P>
<H3><A name="Edit_Field_Dialog"></A>Edit Field Dialog</H3>
<H3><A name="Edit_Field_Dialog"></A><A name="Quick_Edit_Field"></A>Edit Field Dialog</H3>
<P align="center"><IMG src="images/EditFieldDialog.png" alt=""> &nbsp;</P>
<UL>
<LI><B>Field Name</B>: The name of the structure or union field can be changed here.</LI>
<LI><B>Comment</B>: The comment for the field can be entered or changed here.</LI>
<LI><B>DataType</B>: The data can be changed here. The text field is read only so you must
press the ... button to bring up the datatype chooser to change the datatype.</LI>
<LI><B>Add Current Address</B>: If selected, the current address where this field is edited
will be added to the datatype's field comment if not already there.</LI>
<LI><B>Add Today's Date</B>: If selected, the current date will be added to the datatype's
field comment if not already there.</LI>
<P><IMG src="help/shared/note.png"> If a default field (a field with an undefined
datatype (??)) is named or given a comment, the datatype will be set to <B>undefined1</B> if
no specific datatype is set. This is because undefined fields are not stored and therefore
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

@@ -85,7 +85,7 @@ public class DataPlugin extends Plugin implements DataService {
private static final String BASIC_DATA_GROUP = "BasicData";
private static final String DATA_MENU_POPUP_PATH = "Data";
private static final String[] EDIT_DATA_TYPE_POPUP_PATH =
{ DATA_MENU_POPUP_PATH, "Edit Data Type..." };
{ DATA_MENU_POPUP_PATH, "Edit Data Type" };
private static final String[] DATA_SETTINGS_POPUP_PATH =
{ DATA_MENU_POPUP_PATH, "Settings..." };
private static final String[] DEFAULT_SETTINGS_POPUP_PATH =
@@ -95,19 +95,15 @@ public class DataPlugin extends Plugin implements DataService {
{ DATA_MENU_POPUP_PATH, "Choose Data Type..." };
private DataTypeManagerService dtmService;
private DataTypeManagerChangeListenerAdapter adapter;
private DataAction pointerAction;
private DataAction recentlyUsedAction;
private DockingAction editDataTypeAction;
private CreateStructureAction createStructureAction;
private CreateArrayAction createArrayAction;
private List<DataAction> favoriteActions = new ArrayList<>();
private ChooseDataTypeAction chooseDataTypeAction;
private DataTypeManagerChangeListenerAdapter adapter;
private List<DataAction> favoriteActions = new ArrayList<>();
private SwingUpdateManager favoritesUpdateManager;
public DataPlugin(PluginTool tool) {
@@ -150,16 +146,20 @@ public class DataPlugin extends Plugin implements DataService {
pointerAction = new PointerDataAction(this);
tool.addAction(pointerAction);
new ActionBuilder("Edit Field", getName())
.popupMenuPath("Data", "Edit Field")
new ActionBuilder("Quick Edit Field", getName())
.helpLocation(new HelpLocation("DataPlugin", "Quick_Edit_Field"))
.popupMenuPath("Data", "Quick Edit Field...")
.popupMenuGroup("BasicData")
.keyBinding("ctrl shift E")
.sharedKeyBinding()
.withContext(ListingActionContext.class)
.enabledWhen(this::canEditField)
.onAction(this::editField)
.buildAndInstall(tool);
// Data instance settings action based upon data selection in listing
new ActionBuilder("Data Settings", getName()).sharedKeyBinding()
new ActionBuilder("Data Settings", getName())
.sharedKeyBinding()
.popupMenuPath(DATA_SETTINGS_POPUP_PATH)
.popupMenuGroup("Settings")
.withContext(ListingActionContext.class)
@@ -168,7 +168,8 @@ public class DataPlugin extends Plugin implements DataService {
.buildAndInstall(tool);
// Default settings action based upon data selection in listing
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
new ActionBuilder("Default Settings", getName())
.sharedKeyBinding()
.popupMenuPath(DEFAULT_SETTINGS_POPUP_PATH)
.popupMenuGroup("Settings")
.withContext(ListingActionContext.class)
@@ -177,7 +178,8 @@ public class DataPlugin extends Plugin implements DataService {
.buildAndInstall(tool);
// Default settings action for selected datatypes from datatype manager
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
new ActionBuilder("Default Settings", getName())
.sharedKeyBinding()
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
.popupMenuGroup("Settings")
.withContext(DataTypesActionContext.class)
@@ -195,7 +197,8 @@ public class DataPlugin extends Plugin implements DataService {
.buildAndInstall(tool);
// Default settings action for composite editor components (stand-alone archive)
new ActionBuilder("Default Settings", getName()).sharedKeyBinding()
new ActionBuilder("Default Settings", getName())
.sharedKeyBinding()
.popupMenuPath(DATATYPE_SETTINGS_POPUP_PATH)
.popupMenuGroup("Settings")
.withContext(ComponentStandAloneActionContext.class)
@@ -204,14 +207,15 @@ public class DataPlugin extends Plugin implements DataService {
.buildAndInstall(tool);
editDataTypeAction =
new ActionBuilder("Edit Data Type", getName()).popupMenuPath(EDIT_DATA_TYPE_POPUP_PATH)
new ActionBuilder("Edit Data Type", getName())
.popupMenuPath(EDIT_DATA_TYPE_POPUP_PATH)
.popupMenuGroup("BasicData")
.withContext(ListingActionContext.class)
.enabledWhen(c -> {
DataType editableDt = getEditableDataTypeFromContext(c);
if (editableDt != null) {
editDataTypeAction
.setHelpLocation(dtmService.getEditorHelpLocation(editableDt));
HelpLocation helps = dtmService.getEditorHelpLocation(editableDt);
editDataTypeAction.setHelpLocation(helps);
return true;
}
return false;
@@ -293,7 +297,6 @@ public class DataPlugin extends Plugin implements DataService {
@Override
public boolean createData(DataType dt, ListingActionContext context, boolean stackPointers,
boolean enableConflictHandling) {
// TODO: conflict handler (i.e., removal of other conflicting data not yet supported)
ProgramLocation location = context.getLocation();
if (!(location instanceof CodeUnitLocation)) {
return false;
@@ -327,7 +330,7 @@ public class DataPlugin extends Plugin implements DataService {
ProgramLocation location) {
Address start = location.getAddress();
int[] startPath = location.getComponentPath();
Command cmd;
Command<Program> cmd;
if (startPath != null && startPath.length != 0) {
cmd = new CreateDataInStructureCmd(start, startPath, dt, stackPointers);
}
@@ -342,7 +345,7 @@ public class DataPlugin extends Plugin implements DataService {
private boolean createDataForSelection(Program program, DataType dt, boolean stackPointers,
ProgramSelection selection) {
BackgroundCommand cmd;
BackgroundCommand<Program> cmd;
Address start = selection.getMinAddress();
InteriorSelection interSel = selection.getInteriorSelection();
if (interSel != null) {
@@ -837,10 +840,6 @@ public class DataPlugin extends Plugin implements DataService {
return true;
}
public DataType pickDataType() {
return dtmService.getDataType("");
}
private boolean canEditField(ListingActionContext context) {
ProgramLocation location = context.getLocation();
int[] componentPath = location.getComponentPath();
@@ -848,16 +847,23 @@ public class DataPlugin extends Plugin implements DataService {
}
private void editField(ListingActionContext context) {
Program program = context.getProgram();
ProgramLocation location = context.getLocation();
Address address = location.getAddress();
int[] path = location.getComponentPath();
DataTypeComponent component = DataTypeUtils.getDataTypeComponent(program, address, path);
if (component != null) {
EditDataFieldDialog dialog =
new EditDataFieldDialog(tool, dtmService, location, component);
tool.showDialog(dialog);
DataTypeComponent dtc = DataTypeUtils.getDataTypeComponent(program, address, path);
if (dtc == null) {
return;
}
DataType parent = dtc.getParent();
Composite composite = (Composite) parent;
int ordinal = dtc.getOrdinal();
EditDataFieldDialog dialog =
new EditDataFieldDialog(tool, dtmService, composite, program, address, ordinal);
tool.showDialog(dialog);
}
}
@@ -17,6 +17,8 @@ package ghidra.app.plugin.core.data;
import static org.junit.Assert.*;
import java.util.Date;
import org.junit.*;
import docking.action.DockingActionIf;
@@ -27,6 +29,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
import ghidra.test.*;
import ghidra.util.DateUtils;
public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
@@ -50,7 +53,7 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
program = buildProgram();
env.open(program);
env.showTool();
editFieldAction = getAction(plugin, "Edit Field");
editFieldAction = getAction(plugin, "Quick Edit Field");
Data dataAt = program.getListing().getDataAt(addr(0x100));
structure = (Structure) dataAt.getDataType();
codeBrowser.toggleOpen(dataAt);
@@ -118,6 +121,29 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals("char", structure.getComponent(4).getDataType().getDisplayName());
}
@Test
public void testEditDefinedFieldDataTypeAndNameAndComment() {
goTo(0x104);
showFieldEditDialog();
DataTypeComponent dtc = structure.getComponent(4);
assertEquals("word", dtc.getDataType().getDisplayName());
assertEquals("word", getDataTypeText());
setDataType(new CharDataType());
setNameText("TestName");
setCommentText("Flux capacitor relay");
pressOk();
waitForTasks();
assertFalse(isDialogVisible());
dtc = structure.getComponent(4);
assertEquals("char", dtc.getDataType().getDisplayName());
assertEquals("TestName", dtc.getFieldName());
assertEquals("Flux capacitor relay", dtc.getComment());
}
@Test
public void testEditUndefinedFieldName() {
goTo(0x101);
@@ -162,6 +188,49 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals("byte", structure.getComponent(1).getDataType().getDisplayName());
}
@Test
public void testAddAddressCheckbox() {
goTo(0x101);
showFieldEditDialog();
assertEquals("", getCommentText());
pressButtonByText(dialog.getComponent(), "Add Current Address");
assertEquals("00000101", getCommentText());
pressOk();
waitForTasks();
assertEquals("00000101", structure.getComponent(1).getComment());
showFieldEditDialog();
assertEquals("00000101", getCommentText());
pressButtonByText(dialog.getComponent(), "Add Current Address");
assertEquals("", getCommentText());
pressOk();
assertNull(structure.getComponent(1).getComment());
}
@Test
public void testAddDateCheckbox() {
String today = DateUtils.formatCompactDate(new Date());
goTo(0x101);
showFieldEditDialog();
assertEquals("", getCommentText());
pressButtonByText(dialog.getComponent(), "Add Today's Date");
assertEquals(today, getCommentText());
pressOk();
waitForTasks();
assertEquals(today, structure.getComponent(1).getComment());
showFieldEditDialog();
assertEquals(today, getCommentText());
pressButtonByText(dialog.getComponent(), "Add Today's Date");
assertEquals("", getCommentText());
pressOk();
assertNull(structure.getComponent(1).getComment());
}
private boolean isDialogVisible() {
return runSwing(() -> dialog.isVisible());
}
@@ -182,7 +251,7 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
}
private void pressOk() {
runSwing(() -> dialog.okCallback());
pressButtonByText(dialog, "OK");
}
private String getNameText() {
@@ -208,8 +277,4 @@ public class EditFieldDialogTest extends AbstractGhidraHeadedIntegrationTest {
private void setDataType(DataType dataType) {
runSwing(() -> dialog.setDataType(dataType));
}
private String getDialogStatusText() {
return runSwing(() -> dialog.getStatusText());
}
}
@@ -960,6 +960,10 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
EditDataTypeAction editDataTypeAction = new EditDataTypeAction();
setGroupInfo(editDataTypeAction, variableGroup, subGroupPosition++);
// shows the quick editor dialog
EditFieldAction editFieldAction = new EditFieldAction();
setGroupInfo(editFieldAction, variableGroup, subGroupPosition++);
//
// Listing action for Creating Structure on a Variable
//
@@ -1150,6 +1154,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
addLocalAction(decompilerCreateStructureAction);
tool.addAction(listingCreateStructureAction);
addLocalAction(editDataTypeAction);
addLocalAction(editFieldAction);
addLocalAction(specifyCProtoAction);
addLocalAction(overrideSigAction);
addLocalAction(editOverrideSigAction);
@@ -0,0 +1,131 @@
/* ###
* 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.actions;
import docking.ActionContext;
import docking.action.*;
import ghidra.app.decompiler.ClangFieldToken;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.plugin.core.data.EditDataFieldDialog;
import ghidra.app.plugin.core.decompile.DecompilerActionContext;
import ghidra.app.services.DataTypeManagerService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.util.*;
/**
* Performs a quick edit of a given field using the {@link EditDataFieldDialog}. This action is
* similar to the same named action available in the Listing.
*/
public class EditFieldAction extends AbstractDecompilerAction {
public EditFieldAction() {
super("Quick Edit Field", KeyBindingType.SHARED);
setHelpLocation(new HelpLocation("DataPlugin", "Edit_Field_Dialog"));
setPopupMenuData(new MenuData(new String[] { "Quick Edit Field..." }, "Decompile"));
setKeyBindingData(new KeyBindingData("ctrl shift E"));
}
@Override
public boolean isValidContext(ActionContext context) {
return (context instanceof DecompilerActionContext);
}
@Override
protected boolean isEnabledForDecompilerContext(DecompilerActionContext context) {
Function function = context.getFunction();
if (function instanceof UndefinedFunction) {
return false;
}
Address address = context.getAddress();
if (address == null) {
return false;
}
ClangToken tokenAtCursor = context.getTokenAtCursor();
if (tokenAtCursor == null) {
return false;
}
if (!(tokenAtCursor instanceof ClangFieldToken)) {
return false;
}
Composite composite = getCompositeDataType(tokenAtCursor);
if (composite == null) {
return false;
}
int offset = ((ClangFieldToken) tokenAtCursor).getOffset();
if (offset < 0 || offset >= composite.getLength()) {
return false;
}
return true;
}
@Override
protected void decompilerActionPerformed(DecompilerActionContext context) {
ClangToken tokenAtCursor = context.getTokenAtCursor();
Composite composite = getCompositeDataType(tokenAtCursor);
ClangFieldToken token = (ClangFieldToken) tokenAtCursor;
DataTypeComponent dtc = null;
int offset = token.getOffset();
String fieldName = token.getText();
if (composite instanceof Structure structure) {
dtc = structure.getComponentContaining(offset);
}
else if (composite instanceof Union union) {
int n = union.getNumComponents();
for (int i = 0; i < n; i++) {
DataTypeComponent unionDtc = union.getComponent(i);
String dtcName = unionDtc.getFieldName();
if (fieldName.equals(dtcName)) {
dtc = unionDtc;
break;
}
}
}
if (dtc == null) {
Msg.debug(this,
"Unable to find field '%s' at offset %d in composite %s".formatted(fieldName,
offset, composite.getName()));
return;
}
Address address = context.getAddress();
Program program = context.getProgram();
int ordinal = dtc.getOrdinal();
PluginTool tool = context.getTool();
DataTypeManagerService service =
tool.getService(DataTypeManagerService.class);
EditDataFieldDialog dialog =
new EditDataFieldDialog(tool, service, composite, program, address, ordinal);
tool.showDialog(dialog);
}
}
@@ -0,0 +1,292 @@
/* ###
* 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.Before;
import org.junit.Test;
import docking.ActionContext;
import docking.action.DockingActionIf;
import ghidra.app.plugin.core.data.EditDataFieldDialog;
import ghidra.program.model.data.*;
public class DecompilerEditDataFieldTest extends AbstractDecompilerTest {
private static final long INIT_STRING_ADDR = 0X080483c7;
private DockingActionIf editFieldAction;
@Override
@Before
public void setUp() throws Exception {
super.setUp();
editFieldAction = getAction(decompiler, "Quick Edit Field");
}
@Override
protected String getProgramName() {
return "elf/CentOS/32bit/decomp.gzf";
}
@Test
public void testActionEnablement() throws Exception {
/*
Decomp of 'init_string':
1|
2| void init_string(mystring *ptr)
3|
4| {
5| ptr->alloc = 0;
6| return;
7| }
8|
*/
decompile(INIT_STRING_ADDR);
//
// Action should not enabled unless on the data type
//
// Empty line
int line = 1;
int charPosition = 0;
setDecompilerLocation(line, charPosition);
assertActionNotInPopup();
// Signature - first param; a data type
line = 2;
charPosition = 17;
setDecompilerLocation(line, charPosition);
assertActionNotInPopup();
// Signature - first param name
line = 2;
charPosition = 26;
setDecompilerLocation(line, charPosition);
assertActionNotInPopup();
// Syntax - {
line = 4;
charPosition = 0;
setDecompilerLocation(line, charPosition);
assertActionNotInPopup();
// Data access - the data type itself
line = 5;
charPosition = 2;
setDecompilerLocation(line, charPosition);
assertActionNotInPopup();
// Data access - the data type field dereference
line = 5;
charPosition = 7;
setDecompilerLocation(line, charPosition);
assertActionInPopup();
}
@Test
public void testEditName() {
/*
Decomp of 'init_string':
1|
2| void init_string(mystring *ptr)
3|
4| {
5| ptr->alloc = 0;
6| return;
7| }
8|
*/
decompile(INIT_STRING_ADDR);
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
// Data access - the data type field dereference
int line = 5;
int charPosition = 7;
setDecompilerLocation(line, charPosition);
assertToken("alloc", line, charPosition);
EditDataFieldDialog dialog = performEditField();
assertEquals("alloc", structure.getComponent(0).getFieldName());
assertEquals("alloc", getNameText(dialog));
setNameText(dialog, "weight");
pressOk(dialog);
waitForDecompiler();
setDecompilerLocation(line, charPosition);
assertToken("weight", line, charPosition);
assertEquals("weight", structure.getComponent(0).getFieldName());
}
@Test
public void testEditDataType() {
/*
Decomp of 'init_string':
1|
2| void init_string(mystring *ptr)
3|
4| {
5| ptr->alloc = 0;
6| return;
7| }
8|
*/
decompile(INIT_STRING_ADDR);
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
// Data access - the data type field dereference
int line = 5;
int charPosition = 7;
setDecompilerLocation(line, charPosition);
assertToken("alloc", line, charPosition);
EditDataFieldDialog dialog = performEditField();
assertEquals("int", structure.getComponent(0).getDataType().getDisplayName());
assertEquals("int", getDataTypeText(dialog));
setDataType(dialog, new DWordDataType());
pressOk(dialog);
waitForDecompiler();
setDecompilerLocation(line, charPosition);
assertToken("alloc", line, charPosition);
assertEquals("dword", structure.getComponent(0).getDataType().getDisplayName());
}
@Test
public void testEditComment() {
/*
Decomp of 'init_string':
1|
2| void init_string(mystring *ptr)
3|
4| {
5| ptr->alloc = 0;
6| return;
7| }
8|
*/
decompile(INIT_STRING_ADDR);
ProgramBasedDataTypeManager dtm = program.getDataTypeManager();
Structure structure = (Structure) dtm.getDataType(new DataTypePath("/", "mystring"));
// Data access - the data type field dereference
int line = 5;
int charPosition = 7;
setDecompilerLocation(line, charPosition);
assertToken("alloc", line, charPosition);
EditDataFieldDialog dialog = performEditField();
assertEquals(null, structure.getComponent(0).getComment());
assertEquals("", getCommentText(dialog));
setCommentText(dialog, "comment");
pressOk(dialog);
waitForDecompiler();
setDecompilerLocation(line, charPosition);
assertToken("alloc", line, charPosition);
assertEquals("comment", structure.getComponent(0).getComment());
}
//=================================================================================================
// Private Methods
//=================================================================================================
private EditDataFieldDialog performEditField() {
DecompilerActionContext context =
new DecompilerActionContext(provider, addr(0x0), false);
performAction(editFieldAction, context, false);
return waitForDialogComponent(EditDataFieldDialog.class);
}
private void pressOk(EditDataFieldDialog dialog) {
pressButtonByText(dialog, "OK");
}
private String getNameText(EditDataFieldDialog dialog) {
return runSwing(() -> dialog.getNameText());
}
private void setNameText(EditDataFieldDialog dialog, String newName) {
runSwing(() -> dialog.setNameText(newName));
}
private String getCommentText(EditDataFieldDialog dialog) {
return runSwing(() -> dialog.getCommentText());
}
private void setCommentText(EditDataFieldDialog dialog, String newName) {
runSwing(() -> dialog.setCommentText(newName));
}
private String getDataTypeText(EditDataFieldDialog dialog) {
return runSwing(() -> dialog.getDataTypeText());
}
private void setDataType(EditDataFieldDialog dialog, DataType dataType) {
runSwing(() -> dialog.setDataType(dataType));
}
private void assertActionInPopup() {
ActionContext context = provider.getActionContext(null);
assertTrue("'Edit Field' action should be enabled; currently selected token: " +
provider.currentTokenToString(), editFieldAction.isAddToPopup(context));
}
private void assertActionNotInPopup() {
ActionContext context = provider.getActionContext(null);
assertFalse(
"'Edit Field' action should not be enabled; currently selected token: " +
provider.currentTokenToString(),
editFieldAction.isAddToPopup(context));
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -29,7 +29,7 @@ import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.*;
public class EquateTest extends AbstractDecompilerTest {
public class DecompilerEquateTest extends AbstractDecompilerTest {
private static class EquateNameForce extends SetEquateAction {
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,7 +40,7 @@ import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
public class HighSymbolTest extends AbstractDecompilerTest {
public class DecompilerHighSymbolTest extends AbstractDecompilerTest {
@Override
protected String getProgramName() {
return "Winmine__XP.exe.gzf";
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -40,7 +40,7 @@ import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.xml.XmlParseException;
public class SpecExtensionTest extends AbstractDecompilerTest {
public class DecompilerSpecExtensionTest extends AbstractDecompilerTest {
@Override
protected String getProgramName() {
return "Winmine__XP.exe.gzf";
@@ -166,6 +166,18 @@ public abstract class DecompilerReference {
}
}
}
else if (fieldDt instanceof Union union) {
String fieldName = field.getText();
int n = union.getNumComponents();
for (int i = 0; i < n; i++) {
DataTypeComponent unionDtc = union.getComponent(i);
String dtcName = unionDtc.getFieldName();
if (fieldName.equals(dtcName)) {
return unionDtc.getDataType();
}
}
}
return fieldDt;
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -31,6 +31,7 @@ public class DateUtils {
/** Example: Oct 31, 2019 03:24 PM */
private static final String DATE_TIME_FORMAT_STRING = "MMM dd, yyyy hh:mm a";
private static final String DATE_FORMAT_STRING = "MM/dd/yyyy";
private static final String COMPACT_DATE_FORMAT_STRING = "MM/dd/yy";
private static final String TIME_FORMAT_STRING = "h:mm";
private static final DateTimeFormatter DATE_TIME_FORMATTER =
@@ -39,6 +40,8 @@ public class DateUtils {
DateTimeFormatter.ofPattern(DATE_FORMAT_STRING);
private static final DateTimeFormatter TIME_FORMATTER =
DateTimeFormatter.ofPattern(TIME_FORMAT_STRING);
private static final DateTimeFormatter COMPACT_DATE_FORMATTER =
DateTimeFormatter.ofPattern(COMPACT_DATE_FORMAT_STRING);
public static final long MS_PER_SEC = 1000;
public static final long MS_PER_MIN = MS_PER_SEC * 60;
@@ -227,6 +230,16 @@ public class DateUtils {
return DATE_FORMATTER.format(toLocalDate(date));
}
/**
* Formats the given date into a compact date string (mm/dd/yy).
*
* @param date the date to format
* @return the date string
*/
public static String formatCompactDate(Date date) {
return COMPACT_DATE_FORMATTER.format(toLocalDate(date));
}
/**
* Formats the given date into a string that contains the date and time. This is in
* contrast to {@link #formatDate(Date)}, which only returns a date string.
@@ -15,11 +15,16 @@
*/
package help.screenshot;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.JRadioButton;
import org.junit.Test;
import docking.DialogComponentProvider;
import docking.*;
import docking.action.DockingActionIf;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.util.table.GhidraTable;
public class DataPluginScreenShots extends GhidraScreenShotGenerator {
@@ -75,7 +80,10 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
@Test
public void testDefaultSettings() {
positionListingTop(0x40d3a4);
performAction("Default Data Settings", "DataPlugin", false);
ComponentProvider componentProvider = getProvider(CodeViewerProvider.class);
ActionContext actionContext = componentProvider.getActionContext(null);
DockingActionIf action = getAction("Default Settings", actionContext);
performAction(action, actionContext, false);
captureDialog();
}
@@ -86,4 +94,29 @@ public class DataPluginScreenShots extends GhidraScreenShotGenerator {
captureDialog();
}
private DockingActionIf getAction(String name, ActionContext context) {
Set<DockingActionIf> actions = getDataPluginActions(context);
for (DockingActionIf element : actions) {
String actionName = element.getName();
int pos = actionName.indexOf(" (");
if (pos > 0) {
actionName = actionName.substring(0, pos);
}
if (actionName.equals(name)) {
return element;
}
}
return null;
}
private Set<DockingActionIf> getDataPluginActions(ActionContext context) {
Set<DockingActionIf> actions = getActionsByOwner(tool, "DataPlugin");
if (context == null) {
return actions;
}
// assumes returned set may be modified
return actions.stream()
.filter(a -> a.isValidContext(context))
.collect(Collectors.toSet());
}
}