mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 15:40:14 +00:00
Merge remote-tracking branch
'origin/GP-4577-dragonmacher-enum-editor-find-uses--SQUASHED' (Closes #6475)
This commit is contained in:
commit
1371bb50c9
@ -21,7 +21,6 @@ import javax.swing.*;
|
||||
|
||||
import docking.action.*;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.plugin.core.datamgr.editor.DataTypeEditorManager;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.util.HelpLocation;
|
||||
@ -34,8 +33,6 @@ import ghidra.util.HelpLocation;
|
||||
*/
|
||||
abstract public class CompositeEditorTableAction extends DockingAction implements EditorAction {
|
||||
|
||||
private static final String PREFIX = DataTypeEditorManager.EDIT_ACTION_PREFIX;
|
||||
|
||||
protected CompositeEditorProvider provider;
|
||||
protected CompositeEditorModel model;
|
||||
protected String tooltip;
|
||||
@ -57,8 +54,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
|
||||
|
||||
public CompositeEditorTableAction(CompositeEditorProvider provider, String name, String group,
|
||||
String[] popupPath, String[] menuPath, Icon icon) {
|
||||
super(PREFIX + name, provider.plugin.getName(),
|
||||
KeyBindingType.SHARED);
|
||||
super(name, provider.plugin.getName(), KeyBindingType.SHARED);
|
||||
init(provider);
|
||||
if (menuPath != null) {
|
||||
setMenuBarData(new MenuData(menuPath, icon, group));
|
||||
@ -114,11 +110,7 @@ abstract public class CompositeEditorTableAction extends DockingAction implement
|
||||
abstract public void adjustEnablement();
|
||||
|
||||
public String getHelpName() {
|
||||
String actionName = getName();
|
||||
if (actionName.startsWith(PREFIX)) {
|
||||
actionName = actionName.substring(PREFIX.length());
|
||||
}
|
||||
return actionName;
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -24,17 +24,15 @@ import ghidra.program.model.data.DataTypeComponent;
|
||||
import ghidra.util.*;
|
||||
|
||||
/**
|
||||
* An action to show references to the field in the currently selected editor row
|
||||
* An action to show references to the field in the currently selected editor row.
|
||||
*/
|
||||
public class FindReferencesToField extends CompositeEditorTableAction {
|
||||
public class FindReferencesToStructureFieldAction extends CompositeEditorTableAction {
|
||||
|
||||
public final static String ACTION_NAME = "Find Uses of";
|
||||
private final static String GROUP_NAME = BASIC_ACTION_GROUP;
|
||||
private final static String ACTION_NAME = "Find Uses of";
|
||||
private final static String DESCRIPTION = "Find uses of field in the selected row";
|
||||
private static String[] popupPath = new String[] { ACTION_NAME };
|
||||
|
||||
public FindReferencesToField(CompositeEditorProvider provider) {
|
||||
super(provider, ACTION_NAME, GROUP_NAME, popupPath, null, null);
|
||||
public FindReferencesToStructureFieldAction(CompositeEditorProvider provider) {
|
||||
super(provider, ACTION_NAME, BASIC_ACTION_GROUP, new String[] { ACTION_NAME }, null, null);
|
||||
setDescription(DESCRIPTION);
|
||||
adjustEnablement();
|
||||
setHelpLocation(new HelpLocation(HelpTopics.FIND_REFERENCES, "Data_Types"));
|
||||
@ -46,8 +44,8 @@ public class FindReferencesToField extends CompositeEditorTableAction {
|
||||
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
|
||||
if (service == null) {
|
||||
Msg.showError(this, null, "Missing Plugin",
|
||||
"The FindAppliedDataTypesService is not installed.\n" +
|
||||
"Please add the plugin implementing this service.");
|
||||
"The %s is not installed.\nPlease add the plugin implementing this service."
|
||||
.formatted(FindAppliedDataTypesService.class.getSimpleName()));
|
||||
return;
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class StructureEditorProvider extends CompositeEditorProvider {
|
||||
new DeleteAction(this),
|
||||
new PointerAction(this),
|
||||
new ArrayAction(this),
|
||||
new FindReferencesToField(this),
|
||||
new FindReferencesToStructureFieldAction(this),
|
||||
new UnpackageAction(this),
|
||||
new EditComponentAction(this),
|
||||
new EditFieldAction(this),
|
||||
|
@ -212,7 +212,7 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
||||
|
||||
// ZVeryLast group
|
||||
addLocalAction(new FindReferencesToDataTypeAction(plugin)); // DataType
|
||||
addLocalAction(new FindReferencesToFieldAction(plugin)); // DataType
|
||||
addLocalAction(new FindReferencesToFieldByNameOrOffsetAction(plugin)); // DataType
|
||||
addLocalAction(new FindBaseDataTypeAction(plugin)); // DataType
|
||||
addLocalAction(new DisplayTypeAsGraphAction(plugin));
|
||||
|
||||
|
@ -0,0 +1,79 @@
|
||||
/* ###
|
||||
* 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.datamgr.actions;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.util.*;
|
||||
|
||||
public abstract class AbstractFindReferencesToFieldAction extends DockingAction {
|
||||
|
||||
// The base action name will be used to create the menu item by appending the field name
|
||||
public static final String BASE_ACTION_NAME = "Find Uses of";
|
||||
|
||||
private Plugin plugin;
|
||||
|
||||
public AbstractFindReferencesToFieldAction(Plugin plugin) {
|
||||
super(BASE_ACTION_NAME, plugin.getName(), KeyBindingType.SHARED);
|
||||
this.plugin = plugin;
|
||||
|
||||
String menuGroup = "ZVeryLast"; // it's own group; on the bottom
|
||||
setPopupMenuData(new MenuData(new String[] { "Find Uses of Field..." }, null, menuGroup));
|
||||
|
||||
setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types"));
|
||||
}
|
||||
|
||||
protected abstract DataTypeAndFields getSelectedType(ActionContext context);
|
||||
|
||||
protected abstract FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields);
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
return getSelectedType(context) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
||||
ServiceProvider serviceProvider = plugin.getTool();
|
||||
FindAppliedDataTypesService service =
|
||||
serviceProvider.getService(FindAppliedDataTypesService.class);
|
||||
if (service == null) {
|
||||
Msg.showError(this, null, "Missing Plugin",
|
||||
"The %s is not installed.\nPlease add the plugin implementing this service."
|
||||
.formatted(FindAppliedDataTypesService.class.getSimpleName()));
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeAndFields typeAndFields = getSelectedType(context);
|
||||
FieldMatcher fieldMatcher = createFieldMatcher(typeAndFields);
|
||||
if (fieldMatcher == null) {
|
||||
return; // user cancelled
|
||||
}
|
||||
|
||||
DataType dt = fieldMatcher.getDataType();
|
||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
|
||||
}
|
||||
|
||||
public record DataTypeAndFields(DataType dataType, String[] fieldNames) {
|
||||
// record
|
||||
}
|
||||
}
|
@ -23,106 +23,80 @@ import javax.swing.tree.TreePath;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.tree.GTree;
|
||||
import docking.widgets.tree.GTreeNode;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypesActionContext;
|
||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||
import ghidra.app.plugin.core.navigation.FindAppliedDataTypesService;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.data.Enum;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
public class FindReferencesToFieldAction extends DockingAction {
|
||||
/**
|
||||
* An action that can be used on a {@link Composite} or {@link Enum} to find references to a field
|
||||
* by name or offset.
|
||||
*/
|
||||
public class FindReferencesToFieldByNameOrOffsetAction extends AbstractFindReferencesToFieldAction {
|
||||
|
||||
private final DataTypeManagerPlugin plugin;
|
||||
|
||||
public FindReferencesToFieldAction(DataTypeManagerPlugin plugin) {
|
||||
super("Find Uses of Field", plugin.getName());
|
||||
this.plugin = plugin;
|
||||
|
||||
String menuGroup = "ZVeryLast"; // it's own group; on the bottom
|
||||
setPopupMenuData(new MenuData(new String[] { "Find Uses of Field..." }, null, menuGroup));
|
||||
|
||||
setHelpLocation(new HelpLocation("LocationReferencesPlugin", "Data_Types"));
|
||||
setEnabled(true);
|
||||
public FindReferencesToFieldByNameOrOffsetAction(Plugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
protected DataTypeAndFields getSelectedType(ActionContext context) {
|
||||
if (!(context instanceof DataTypesActionContext)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gtree = (GTree) contextObject;
|
||||
TreePath[] selectionPaths = gtree.getSelectionPaths();
|
||||
if (selectionPaths.length != 1) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent();
|
||||
if (!(node instanceof DataTypeNode)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
DataTypeNode dtNode = (DataTypeNode) node;
|
||||
DataType dataType = dtNode.getDataType();
|
||||
return dataType instanceof Composite || dataType instanceof Enum;
|
||||
DataType dt = dtNode.getDataType();
|
||||
if (!(dt instanceof Composite || dt instanceof Enum)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] fields = null;
|
||||
if (dt instanceof Composite) {
|
||||
fields = getCompositeFieldNames((Composite) dt);
|
||||
}
|
||||
else if (dt instanceof Enum) {
|
||||
fields = ((Enum) dt).getNames();
|
||||
}
|
||||
|
||||
return new DataTypeAndFields(dt, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
GTree gTree = (GTree) context.getContextObject();
|
||||
TreePath[] selectionPaths = gTree.getSelectionPaths();
|
||||
final DataTypeNode dataTypeNode = (DataTypeNode) selectionPaths[0].getLastPathComponent();
|
||||
|
||||
PluginTool tool = plugin.getTool();
|
||||
FindAppliedDataTypesService service = tool.getService(FindAppliedDataTypesService.class);
|
||||
if (service == null) {
|
||||
Msg.showError(this, null, "Missing Plugin",
|
||||
"The FindAppliedDataTypesService is not installed.\n" +
|
||||
"Please add the plugin implementing this service.");
|
||||
return;
|
||||
}
|
||||
|
||||
DataType dt = dataTypeNode.getDataType();
|
||||
String[] choices = null;
|
||||
if (dt instanceof Composite) {
|
||||
choices = getCompisiteFieldNames((Composite) dt);
|
||||
}
|
||||
else if (dt instanceof Enum) {
|
||||
choices = ((Enum) dt).getNames();
|
||||
}
|
||||
protected FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields) {
|
||||
|
||||
DataType dt = typeAndFields.dataType();
|
||||
String message = "Find uses of '" + dt.getName() + "' field by name or offset";
|
||||
String userChoice = OptionDialog.showEditableInputChoiceDialog(null, "Choose Field",
|
||||
message, choices, null, OptionDialog.QUESTION_MESSAGE);
|
||||
message, typeAndFields.fieldNames(), null, OptionDialog.QUESTION_MESSAGE);
|
||||
if (userChoice == null) {
|
||||
return;
|
||||
return null; // cancelled
|
||||
}
|
||||
|
||||
FieldMatcher fieldMatcher;
|
||||
Long longChoice = parseInt(userChoice);
|
||||
if (longChoice != null) {
|
||||
fieldMatcher = new FieldMatcher(dt, longChoice.intValue());
|
||||
return new FieldMatcher(dt, longChoice.intValue());
|
||||
}
|
||||
else {
|
||||
fieldMatcher = new FieldMatcher(dt, userChoice);
|
||||
}
|
||||
|
||||
Swing.runLater(() -> service.findAndDisplayAppliedDataTypeAddresses(dt, fieldMatcher));
|
||||
return new FieldMatcher(dt, userChoice);
|
||||
}
|
||||
|
||||
private Long parseInt(String s) {
|
||||
return NumericUtilities.parseNumber(s, null);
|
||||
}
|
||||
|
||||
private String[] getCompisiteFieldNames(Composite composite) {
|
||||
private String[] getCompositeFieldNames(Composite composite) {
|
||||
DataTypeComponent[] components = composite.getDefinedComponents();
|
||||
List<String> names = new ArrayList<>();
|
||||
for (DataTypeComponent dataTypeComponent : components) {
|
||||
@ -139,4 +113,7 @@ public class FindReferencesToFieldAction extends DockingAction {
|
||||
return names.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private Long parseInt(String s) {
|
||||
return NumericUtilities.parseNumber(s, null);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import docking.actions.DockingToolActions;
|
||||
import docking.actions.SharedDockingActionPlaceholder;
|
||||
import ghidra.app.plugin.core.compositeeditor.*;
|
||||
import ghidra.app.plugin.core.datamgr.DataTypeManagerPlugin;
|
||||
import ghidra.app.plugin.core.datamgr.actions.AbstractFindReferencesToFieldAction;
|
||||
import ghidra.app.plugin.core.function.AbstractEditFunctionSignatureDialog;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -145,7 +146,7 @@ public class DataTypeEditorManager implements EditorListener {
|
||||
registerAction(DeleteAction.ACTION_NAME);
|
||||
registerAction(PointerAction.ACTION_NAME);
|
||||
registerAction(ArrayAction.ACTION_NAME);
|
||||
registerAction(FindReferencesToField.ACTION_NAME);
|
||||
registerAction(AbstractFindReferencesToFieldAction.BASE_ACTION_NAME);
|
||||
registerAction(UnpackageAction.ACTION_NAME);
|
||||
registerAction(EditComponentAction.ACTION_NAME);
|
||||
registerAction(EditFieldAction.ACTION_NAME);
|
||||
|
@ -300,9 +300,8 @@ class EnumEditorPanel extends JPanel {
|
||||
table.setRowHeight(table.getRowHeight() + 4);
|
||||
table.setDefaultEditor(String.class, new EnumStringCellEditor());
|
||||
table.getColumnModel()
|
||||
.getColumn(EnumTableModel.VALUE_COL)
|
||||
.setCellEditor(
|
||||
new EnumLongCellEditor());
|
||||
.getColumn(EnumTableModel.VALUE_COL)
|
||||
.setCellEditor(new EnumLongCellEditor());
|
||||
table.setDefaultRenderer(String.class, new GTableCellRenderer());
|
||||
table.setDefaultRenderer(Long.class, new EnumValueRenderer());
|
||||
add(createInfoPanel(), BorderLayout.SOUTH);
|
||||
@ -483,6 +482,16 @@ class EnumEditorPanel extends JPanel {
|
||||
showValuesAsHex = showHex;
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
String getSelectedFieldName() {
|
||||
|
||||
int row = table.getSelectedRow();
|
||||
if (row < 0) {
|
||||
return null;
|
||||
}
|
||||
EnumEntry enumEntry = tableModel.getRowObject(row);
|
||||
return enumEntry.getName();
|
||||
}
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
@ -246,13 +246,21 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
tool.setStatusInfo(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTypeManager getDataTypeManager() {
|
||||
return dataTypeManager;
|
||||
}
|
||||
|
||||
String getCategoryText() {
|
||||
return dataTypeManager.getName() + originalCategoryPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTypeManager getDataTypeManager() {
|
||||
return dataTypeManager;
|
||||
Enum getEnum() {
|
||||
return originalEnum;
|
||||
}
|
||||
|
||||
String getSelectedFieldName() {
|
||||
return editorPanel.getSelectedFieldName();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
@ -317,10 +325,13 @@ public class EnumEditorProvider extends ComponentProviderAdapter
|
||||
showEnumAction.setToolBarData(
|
||||
new ToolBarData(new GIcon("icon.plugin.enum.editor.home"), thirdGroup));
|
||||
|
||||
FindReferencesToEnumFieldAction findReferencesAction = new FindReferencesToEnumFieldAction(plugin);
|
||||
|
||||
tool.addLocalAction(this, applyAction);
|
||||
tool.addLocalAction(this, addAction);
|
||||
tool.addLocalAction(this, deleteAction);
|
||||
tool.addLocalAction(this, showEnumAction);
|
||||
tool.addLocalAction(this, findReferencesAction);
|
||||
}
|
||||
|
||||
private boolean applyChanges() {
|
||||
|
@ -0,0 +1,67 @@
|
||||
/* ###
|
||||
* 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.datamgr.editor;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ComponentProvider;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.core.datamgr.actions.AbstractFindReferencesToFieldAction;
|
||||
import ghidra.app.services.FieldMatcher;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.data.DataType;
|
||||
|
||||
/**
|
||||
* Finds references to a member of an enum.
|
||||
*/
|
||||
public class FindReferencesToEnumFieldAction extends AbstractFindReferencesToFieldAction {
|
||||
|
||||
public FindReferencesToEnumFieldAction(Plugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataTypeAndFields getSelectedType(ActionContext context) {
|
||||
|
||||
ComponentProvider provider = context.getComponentProvider();
|
||||
if (!(provider instanceof EnumEditorProvider enumProvider)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String fieldName = enumProvider.getSelectedFieldName();
|
||||
if (fieldName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
updateMenuName(fieldName);
|
||||
DataType dt = enumProvider.getEnum();
|
||||
return new DataTypeAndFields(dt, new String[] { fieldName });
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FieldMatcher createFieldMatcher(DataTypeAndFields typeAndFields) {
|
||||
DataType dt = typeAndFields.dataType();
|
||||
String field = typeAndFields.fieldNames()[0];
|
||||
return new FieldMatcher(dt, field);
|
||||
}
|
||||
|
||||
private void updateMenuName(String name) {
|
||||
String menuName = BASE_ACTION_NAME + ' ' + name;
|
||||
MenuData data = getPopupMenuData().cloneData();
|
||||
data.setMenuPath(new String[] { menuName });
|
||||
setPopupMenuData(data);
|
||||
}
|
||||
|
||||
}
|
@ -21,7 +21,7 @@ import docking.tool.ToolConstants;
|
||||
|
||||
/**
|
||||
* A marker interface to signal that the implementing action serves as an action that should
|
||||
* not be itself used in the tool, but should only be used to register and manager keybindings.
|
||||
* not itself be used in the tool, but should only be used to register and manage keybindings.
|
||||
*
|
||||
*
|
||||
* <p>This action is merely a tool by which transient components can ensure that their actions
|
||||
|
Loading…
Reference in New Issue
Block a user