Merge remote-tracking branch

'origin/GT-0_ghidravore_fixing_missing_invalide_cache_method_in_FunctionTagManager'

Conflicts:
	Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/tags/FunctionTagsComponentProvider.java
This commit is contained in:
ghidravore 2020-11-05 17:39:50 -05:00
commit 6648cbb8cb
30 changed files with 695 additions and 613 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -18,14 +18,13 @@ package ghidra.app.cmd.function;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
/**
* Command for assigning a tag to a function. Executing this will pop up a dialog
* allowing the user to assign tags to a function.
*
* allowing the user to assign tags to a function.
*/
public class AddFunctionTagCmd implements Command {
@ -34,7 +33,7 @@ public class AddFunctionTagCmd implements Command {
private String errorMsg = "";
/**
* Constructor.
* Constructor
*
* @param tagName the name of the tag to add
* @param entryPoint the function address
@ -44,15 +43,11 @@ public class AddFunctionTagCmd implements Command {
this.entryPoint = entryPoint;
}
/******************************************************************************
* PUBLIC METHODS
******************************************************************************/
@Override
public boolean applyTo(DomainObject obj) {
ProgramDB program = (ProgramDB) obj;
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
Function function = functionManagerDB.getFunctionAt(entryPoint);
FunctionManager functionManager = program.getFunctionManager();
Function function = functionManager.getFunctionAt(entryPoint);
if (function == null) {
errorMsg = "Function not found at: " + entryPoint.toString();

View file

@ -18,26 +18,24 @@ package ghidra.app.cmd.function;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.listing.FunctionTag;
import ghidra.program.model.listing.FunctionTagManager;
import ghidra.program.model.listing.*;
/**
* Updates the name or comment field for a given function tag.
* Updates the name or comment field for a given function tag
*/
public class ChangeFunctionTagCmd implements Command {
private final int field;
private final String tagName;
private final String newVal;
private String errorMsg = "";
public static final int TAG_NAME_CHANGED = 0;
public static final int TAG_COMMENT_CHANGED = 1;
/**
* Constructor.
* Constructor
*
* @param tagName the name of the tag to change
* @param newVal the new value to set
@ -52,16 +50,12 @@ public class ChangeFunctionTagCmd implements Command {
this.field = field;
}
/******************************************************************************
* PUBLIC METHODS
******************************************************************************/
@Override
public boolean applyTo(DomainObject obj) {
ProgramDB program = (ProgramDB) obj;
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
FunctionTagManager functionTagManager = functionManagerDB.getFunctionTagManager();
FunctionTag tag = functionTagManager.getFunctionTag(tagName);
FunctionManager functionManager = program.getFunctionManager();
FunctionTagManager tagManager = functionManager.getFunctionTagManager();
FunctionTag tag = tagManager.getFunctionTag(tagName);
if (tag == null) {
errorMsg = "Function Tag not found: " + tagName;

View file

@ -18,12 +18,11 @@ package ghidra.app.cmd.function;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.FunctionTagManager;
/**
* Command for assigning a tag to a function.
*
* Command for assigning a tag to a function
*/
public class CreateFunctionTagCmd implements Command {
@ -31,7 +30,7 @@ public class CreateFunctionTagCmd implements Command {
private String comment;
/**
* Constructor.
* Constructor
*
* @param name the name of the new tag
*/
@ -41,7 +40,7 @@ public class CreateFunctionTagCmd implements Command {
}
/**
* Constructor.
* Constructor
*
* @param name the name of the new tag
* @param comment the tag comment
@ -51,16 +50,12 @@ public class CreateFunctionTagCmd implements Command {
this.comment = comment;
}
/******************************************************************************
* PUBLIC METHODS
******************************************************************************/
@Override
public boolean applyTo(DomainObject obj) {
ProgramDB program = (ProgramDB) obj;
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
FunctionTagManager functionTagManager = functionManagerDB.getFunctionTagManager();
functionTagManager.createFunctionTag(name, comment);
FunctionManager functionManager = program.getFunctionManager();
FunctionTagManager tagManager = functionManager.getFunctionTagManager();
tagManager.createFunctionTag(name, comment);
return true;
}

View file

@ -18,19 +18,19 @@ package ghidra.app.cmd.function;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.listing.FunctionManager;
import ghidra.program.model.listing.FunctionTag;
import ghidra.util.task.TaskMonitor;
/**
* Command for deleting a tag from the system.
* Command for deleting a tag from the system
*/
public class DeleteFunctionTagCmd extends BackgroundCommand {
private String tagName;
/**
* Constructor.
* Constructor
*
* @param tagName the name of the tag to delete
*/
@ -38,21 +38,15 @@ public class DeleteFunctionTagCmd extends BackgroundCommand {
this.tagName = tagName;
}
/******************************************************************************
* PUBLIC METHODS
******************************************************************************/
@Override
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
ProgramDB program = (ProgramDB) obj;
FunctionManagerDB functionManager = (FunctionManagerDB) program.getFunctionManager();
FunctionManager functionManager = program.getFunctionManager();
FunctionTag tag = functionManager.getFunctionTagManager().getFunctionTag(tagName);
if (tag != null) {
tag.delete();
}
return true;
}

View file

@ -18,13 +18,12 @@ package ghidra.app.cmd.function;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainObject;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionManager;
/**
* Command for removing a tag from a function.
*
* Command for removing a tag from a function
*/
public class RemoveFunctionTagCmd implements Command {
@ -32,6 +31,8 @@ public class RemoveFunctionTagCmd implements Command {
private String tagName;
/**
* Constructor
*
* @param tagName the name of the tag to remove
* @param entryPoint the address of the function
*/
@ -40,19 +41,12 @@ public class RemoveFunctionTagCmd implements Command {
this.entryPoint = entryPoint;
}
/******************************************************************************
* PUBLIC METHODS
******************************************************************************/
@Override
public boolean applyTo(DomainObject obj) {
ProgramDB program = (ProgramDB) obj;
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
Function function = functionManagerDB.getFunctionAt(entryPoint);
FunctionManager functionManager = program.getFunctionManager();
Function function = functionManager.getFunctionAt(entryPoint);
function.removeTag(tagName);
// The remove function does not return a success/fail statutus, so just return
// and move on.
return true;
}

View file

@ -24,7 +24,7 @@ import resources.Icons;
import resources.ResourceManager;
/**
* Provides buttons to be used with the {@link FunctionTagsComponentProvider}.
* Provides buttons to be used with the {@link FunctionTagProvider}.
* These buttons allow users to add or remove tags from functions, or delete
* tags altogether.
* <p>

View file

@ -26,7 +26,7 @@ import ghidra.program.util.ProgramLocation;
/**
* Plugin for managing function tags. This works with the associated
* {@link FunctionTagsComponentProvider} to allow users to view and
* {@link FunctionTagProvider} to allow users to view and
* edit function tags both globally and for individual functions.
*
*/
@ -40,18 +40,18 @@ import ghidra.program.util.ProgramLocation;
)
//@formatter:on
public class FunctionTagPlugin extends ProgramPlugin {
public final static String FUNCTION_TAG_MENU_SUBGROUP = "TagFunction";
// Action visible when right-clicking on a function in the listing.
private EditFunctionTagsAction editFunctionTagsAction;
// The display object for this plugin.
private FunctionTagsComponentProvider provider;
private FunctionTagProvider provider;
public FunctionTagPlugin(PluginTool tool) {
super(tool, true, false);
provider = new FunctionTagsComponentProvider(this, getCurrentProgram());
provider = new FunctionTagProvider(this, getCurrentProgram());
createActions();
}
@ -64,7 +64,7 @@ public class FunctionTagPlugin extends ProgramPlugin {
*
* @return the component provider
*/
public FunctionTagsComponentProvider getProvider() {
public FunctionTagProvider getProvider() {
return provider;
}

View file

@ -16,24 +16,29 @@
package ghidra.app.plugin.core.function.tags;
import java.awt.*;
import java.util.ArrayList;
import java.util.*;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.label.GLabel;
import docking.widgets.textfield.HintTextField;
import ghidra.app.cmd.function.CreateFunctionTagCmd;
import ghidra.app.context.ProgramActionContext;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.model.*;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.util.*;
import ghidra.util.*;
import ghidra.util.task.SwingUpdateManager;
import resources.ResourceManager;
/**
* Displays all the function tags in the database and identifies which ones have
@ -44,13 +49,13 @@ import ghidra.util.*;
* <LI>Edit tags (both name and comment)</LI>
* <LI>Delete tags</LI>
* <LI>Assign tags to the currently selected function</LI>
* <LI>Remove tags from the currently selected function</LI>
* <LI>Remove tags from the currently selected function</LI>
* </UL>
* This provider can be shown by right-clicking on a function and selecting the
* This provider can be shown by right-clicking on a function and selecting the
* "Edit Tags" option, or by selecting the "Edit Function Tags" option from the
* "Window" menu.
*/
public class FunctionTagsComponentProvider extends ComponentProviderAdapter
public class FunctionTagProvider extends ComponentProviderAdapter
implements DomainObjectListener {
private Color BORDER_COLOR = Color.GRAY;
@ -69,27 +74,48 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
private int MIN_WIDTH = 850;
private int MIN_HEIGHT = 350;
// The current program location selected in the listing.
private SwingUpdateManager updater = new SwingUpdateManager(this::doUpdate);
// The current program location selected in the listing.
private ProgramLocation currentLocation = null;
// Character used as a separator when entering multiple tags in
// the create tag entry field.
private static String INPUT_DELIMITER = ",";
private static final String INPUT_DELIMITER = ",";
/**
* Optional! If there is a file with this name which can be found by the
* {@link ResourceManager}, and it contains a valid list of tag names,
* they will be loaded. The file must be XML with the following
* structure:
*
* <tags>
* <tag>
* <name>TAG1</name>
* <comment>tag comment</comment>
* </tag>
* </tags>
*
*/
private static String TAG_FILE = "functionTags.xml";
// Keeps a list of the original tags as loaded from file. This is necessary when switching
// between programs where we need to know the original state of the disabled tags. Without
// this we would need to reload from file on each new program activation.
private Set<FunctionTag> tagsFromFile;
/**
* Constructor
*
*
* @param plugin the function tag plugin
* @param program the current program
*/
public FunctionTagsComponentProvider(FunctionTagPlugin plugin, Program program) {
public FunctionTagProvider(FunctionTagPlugin plugin, Program program) {
super(plugin.getTool(), "Function Tags", plugin.getName(), ProgramActionContext.class);
setHelpLocation(new HelpLocation(plugin.getName(), plugin.getName()));
this.program = program;
mainPanel = createWorkPanel();
addToTool();
}
@ -97,24 +123,9 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
* PUBLIC METHODS
******************************************************************************/
/**
* Completely clears the UI and loads the tag table from scratch. Note that
* the model will be completely reset based on whatever the current location is
* in the listing.
*/
public void reload() {
Swing.runLater(() -> {
tagInputField.setText("");
updateTitle(currentLocation);
updateTagLists();
});
}
@Override
public void componentShown() {
updateTagLists();
updateTitle(currentLocation);
updateView();
}
@Override
@ -122,21 +133,16 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
return mainPanel;
}
HintTextField getTagInputField() {
return tagInputField;
}
/**
* Invoked when a new location has been detected in the listing. When
* Invoked when a new location has been detected in the listing. When
* this happens we need to update the tag list to show what tags are assigned
* at the current location.
*
*
* @param loc the address selected in the listing
*/
public void locationChanged(ProgramLocation loc) {
currentLocation = loc;
updateTitle(loc);
updateTagLists();
updateView();
}
public void programActivated(Program activatedProgram) {
@ -145,7 +151,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
// Add a listener so we pick up domain object change events (add/delete/remove, etc...)
activatedProgram.addListener(this);
updateTagLists();
updateTagViews();
}
public void programDeactivated(Program deactivatedProgram) {
@ -155,31 +161,60 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
/**
* This class needs to listen for changes to the domain object (tag create, delete, etc...)
* so it can update the display accordingly.
*
* so it can update the display accordingly.
*
* @param ev the change event
*/
@Override
public void domainObjectChanged(DomainObjectChangedEvent ev) {
if (ev.containsEvent(ChangeManager.DOCR_FUNCTION_TAG_CHANGED) ||
if (!isVisible()) {
return;
}
if (ev.containsEvent(DomainObject.DO_OBJECT_RESTORED) ||
ev.containsEvent(ChangeManager.DOCR_FUNCTION_TAG_CREATED) ||
ev.containsEvent(ChangeManager.DOCR_FUNCTION_TAG_DELETED) ||
ev.containsEvent(ChangeManager.DOCR_TAG_REMOVED_FROM_FUNCTION) ||
ev.containsEvent(ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION)) {
reload();
updater.updateLater();
return;
}
if (ev.containsEvent(ChangeManager.DOCR_FUNCTION_TAG_CHANGED)) {
repaint();
}
}
/******************************************************************************
* PRIVATE METHODS
******************************************************************************/
private void doUpdate() {
reload();
}
private void reload() {
allFunctionsPanel.refresh();
Function function = getFunction(currentLocation);
sourcePanel.refresh(function);
targetPanel.refresh(function);
}
private void updateView() {
updateTitle(currentLocation);
updateTagViews();
}
private void repaint() {
sourcePanel.repaint();
targetPanel.repaint();
allFunctionsPanel.repaint();
}
private void updateTitle(ProgramLocation location) {
if (!isVisible()) {
return;
}
Function function = getFunctionAtLocation(location);
Function function = getFunction(location);
if (function == null) {
setSubTitle("NOT A FUNCTION");
}
@ -210,8 +245,8 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
allFunctionsPanel.setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
// If we don't set this, then the splitter won't be able to shrink the
// target panels below the size required by its header, which can be large
// because of the amount of text displayed. Keep the minimum size setting on
// target panels below the size required by its header, which can be large
// because of the amount of text displayed. Keep the minimum size setting on
// the source panel, however. That is generally small.
targetPanel.setMinimumSize(new Dimension(0, 0));
@ -236,12 +271,12 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
* Updates the button panel depending on the selection state of the
* tag lists. Also updates the {@link AllFunctionsPanel} so it can update
* its list.
*
*
* @param panel the panel that generated the selection event
*/
public void selectionChanged(TagListPanel panel) {
Function function = getFunctionAtLocation(currentLocation);
Function function = getFunction(currentLocation);
if (panel instanceof SourceTagsPanel) {
buttonPanel.sourcePanelSelectionChanged(function != null);
@ -258,22 +293,70 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
allFunctionsPanel.setSelectedTags(sourceTags);
}
TargetTagsPanel getTargetPanel() {
public AllFunctionsPanel getAllFunctionsPanel() {
return allFunctionsPanel;
}
public TargetTagsPanel getTargetPanel() {
return targetPanel;
}
SourceTagsPanel getSourcePanel() {
public HintTextField getTagInputField() {
return tagInputField;
}
public SourceTagsPanel getSourcePanel() {
return sourcePanel;
}
public FunctionTagButtonPanel getButtonPanel() {
return buttonPanel;
}
public JPanel getInputPanel() {
return inputPanel;
}
Set<FunctionTag> backgroundLoadTags() {
// Add any tags from the file system that are not in the db
List<? extends FunctionTag> dbTags = getAllTagsFromDatabase();
Set<FunctionTag> allTags = new HashSet<>(dbTags);
allTags.addAll(getFileTags());
return allTags;
}
/**
* Returns the {@link Function} at the given program location. If not a function, or
* if the location is not a pointer to a function returns null.
*
* Loads tags from the external file specified.
*
* @return the loaded tags
*/
private Set<FunctionTag> getFileTags() {
if (tagsFromFile == null) {
tagsFromFile = FunctionTagLoader.loadTags(TAG_FILE);
}
return tagsFromFile;
}
/**
* Returns an array of all tags stored in the database.
*
* @return list of tags
*/
private List<? extends FunctionTag> getAllTagsFromDatabase() {
if (program == null) {
return Collections.emptyList();
}
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
return functionManagerDB.getFunctionTagManager().getAllFunctionTags();
}
/**
* Returns the {@link Function} for the given program location
*
* @param loc the program location
* @return function containing the location, or null if not applicable
*/
private Function getFunctionAtLocation(ProgramLocation loc) {
private Function getFunction(ProgramLocation loc) {
Address functionAddress = getFunctionAddress(loc);
if (functionAddress == null) {
@ -285,7 +368,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
/**
* Retrieves the address of the function associated with the given program location.
*
*
* @param loc the program location
* @return the entry point of the function, or null if not valid
*/
@ -295,8 +378,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
return null;
}
// If the user clicks on an instruction within a function we want to show
// the tags.
// If the user clicks on an instruction within a function we want to show the tags
if (program.getFunctionManager().isInFunction(loc.getAddress())) {
return loc.getAddress();
}
@ -311,13 +393,11 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
}
/**
* Refreshes the contents of the table with the current program and location. This
* should be called any time a program is activated or the location in the listing
* has changed.
* Refreshes the contents of the tables with the current program and location
*/
private void updateTagLists() {
private void updateTagViews() {
if (sourcePanel == null || targetPanel == null || allFunctionsPanel == null) {
if (mainPanel == null || !isVisible()) {
return;
}
@ -325,7 +405,7 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
targetPanel.setProgram(program);
allFunctionsPanel.setProgram(program);
// Get the currently selected tags and use them to update the all functions panel. If
// Get the currently selected tags and use them to update the all functions panel. If
// there is no current selection, leave the table as-is.
Set<FunctionTag> sTags = sourcePanel.getSelectedTags();
Set<FunctionTag> tTags = targetPanel.getSelectedTags();
@ -334,53 +414,63 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
allFunctionsPanel.refresh(sTags);
}
Function function = getFunctionAtLocation(currentLocation);
Function function = getFunction(currentLocation);
sourcePanel.refresh(function);
targetPanel.refresh(function);
}
/**
* Parses all items in the text input field and adds them as new tags.
* Parses all items in the text input field and adds them as new tags.
*/
private void processCreates() {
if (program == null) {
Msg.showInfo(this, tool.getActiveWindow(), "No program!",
Msg.showInfo(this, tool.getActiveWindow(), "No Program",
"You must load a program before trying to create tags");
return;
}
List<String> dropped = new ArrayList<>();
List<String> names = getInputNames();
for (String name : names) {
// Only execute the create command if a tag with the given name does not
// already exist.
if (sourcePanel.tagExists(name) || targetPanel.tagExists(name)) {
Msg.showInfo(this, tool.getActiveWindow(), "Duplicate Tag Name",
"There is already a tag with the name [" + name + "]. Please try again.");
// only execute the create command if a tag with the given name does not already exist
// (note: this could fail if the model is not yet loaded, but this is unlikely. The
// only fallout is that the error message would not be shown--the database will not add
// the tag twice.)
if (sourcePanel.tagExists(name)) {
dropped.add(name);
}
else {
Command cmd = new CreateFunctionTagCmd(name);
tool.execute(cmd, program);
}
}
if (!dropped.isEmpty()) {
String text = StringUtils.join(dropped, ", ");
Msg.showInfo(this, tool.getActiveWindow(), "Duplicate Tag Names",
"Tags aleady exist. Ignoring the following tags: " + text);
}
Swing.runLater(() -> tagInputField.setText(""));
}
/**
* Returns a list of tag names the user has entered in the input field.
* Returns a list of tag names the user has entered in the input` field.
* Note: This assumes that multiple entries are comma-delimited.
*
*
* @return the list of tag names to create
*/
private List<String> getInputNames() {
// First split the string on the delimiter to get all the entries.
// first split the string on the delimiter to get all the entries
String[] names = tagInputField.getText().split(INPUT_DELIMITER);
// Trim each item to remove any leading/trailing whitespace and add to
// the return list.
ArrayList<String> nameList = new ArrayList<>();
// trim each item to remove any leading/trailing whitespace and add to the return list
List<String> nameList = new ArrayList<>();
for (String name : names) {
if (!name.trim().isEmpty()) {
if (!StringUtils.isBlank(name)) {
nameList.add(name.trim());
}
}
@ -390,19 +480,25 @@ public class FunctionTagsComponentProvider extends ComponentProviderAdapter
/**
* Creates the text-entry panel for adding new tag names.
*
*
* @return the new text input panel
*/
private JPanel createInputPanel() {
inputPanel = new JPanel(new BorderLayout());
tagInputField = new HintTextField("tag 1, tag 2, ...");
tagInputField.setName("tagInputTF");
tagInputField.addActionListener(e -> processCreates());
inputPanel = new JPanel();
Border outsideBorder = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
Border insideBorder = BorderFactory.createEmptyBorder(5, 2, 2, 2);
inputPanel.setBorder(BorderFactory.createCompoundBorder(outsideBorder, insideBorder));
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.LINE_AXIS));
inputPanel.add(new GLabel(" Create new tag(s):"), BorderLayout.WEST);
inputPanel.add(Box.createHorizontalStrut(5));
inputPanel.add(tagInputField, BorderLayout.CENTER);
return inputPanel;
}
}

View file

@ -22,8 +22,9 @@ class FunctionTagRowObject {
private FunctionTag tag;
private int count;
FunctionTagRowObject(FunctionTag tag) {
FunctionTagRowObject(FunctionTag tag, int count) {
this.tag = tag;
this.count = count;
}
FunctionTag getTag() {

View file

@ -15,10 +15,7 @@
*/
package ghidra.app.plugin.core.function.tags;
import java.util.*;
import java.util.function.Supplier;
import org.apache.commons.collections4.map.LazyMap;
import java.util.Set;
import docking.widgets.table.AbstractDynamicTableColumnStub;
import docking.widgets.table.TableColumnDescriptor;
@ -27,7 +24,6 @@ import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.model.listing.*;
import ghidra.util.datastruct.Accumulator;
import ghidra.util.datastruct.Counter;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -37,12 +33,12 @@ import ghidra.util.task.TaskMonitor;
public class FunctionTagTableModel extends ThreadedTableModel<FunctionTagRowObject, Program> {
private Program program;
private Supplier<Set<FunctionTag>> tagLoader;
private TagListPanel tagListPanel;
protected FunctionTagTableModel(String modelName, ServiceProvider serviceProvider,
Supplier<Set<FunctionTag>> tagLoader) {
TagListPanel tagLoader) {
super(modelName, serviceProvider);
this.tagLoader = tagLoader;
this.tagListPanel = tagLoader;
}
public void setProgram(Program program) {
@ -57,37 +53,15 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTagRowObje
return;
}
Map<String, Counter> countsByName = getFunctionTagCountsByName(monitor);
for (FunctionTag tag : tagLoader.get()) {
FunctionTagRowObject rowObject = new FunctionTagRowObject(tag);
Counter counter = countsByName.get(tag.getName());
rowObject.setCount(counter.count);
accumulator.add(rowObject);
}
}
private Map<String, Counter> getFunctionTagCountsByName(TaskMonitor monitor)
throws CancelledException {
FunctionManager functionManager = program.getFunctionManager();
monitor.initialize(functionManager.getFunctionCount());
Map<String, Counter> map = LazyMap.lazyMap(new HashMap<>(), () -> new Counter());
FunctionIterator it = functionManager.getFunctions(true);
for (Function function : it) {
FunctionTagManager tagManager = functionManager.getFunctionTagManager();
Set<FunctionTag> tags = tagListPanel.backgroundLoadTags();
monitor.initialize(tags.size());
for (FunctionTag tag : tags) {
monitor.checkCanceled();
Set<FunctionTag> functionTags = function.getTags();
for (FunctionTag tag : functionTags) {
String name = tag.getName();
map.get(name).count++;
}
accumulator.add(new FunctionTagRowObject(tag, tagManager.getUseCount(tag)));
monitor.incrementProgress(1);
}
return map;
}
@Override
@ -115,11 +89,23 @@ public class FunctionTagTableModel extends ThreadedTableModel<FunctionTagRowObje
/**
* Returns true if a function tag with a given name is in the model
*
* @param name the tag name to search for
* @param name the tag name
* @return true if the tag exists in the model
*/
public boolean containsTag(String name) {
return getModelData().stream().anyMatch(row -> row.getName().equals(name));
return getRowObject(name) != null;
}
/**
* Returns the row object that matches the given tag name
* @param name the tag name
* @return the row object
*/
public FunctionTagRowObject getRowObject(String name) {
return getAllData().stream()
.filter(row -> row.getName().equals(name))
.findFirst()
.orElseGet(() -> null);
}
/**

View file

@ -23,7 +23,7 @@ import ghidra.program.model.listing.FunctionTag;
* This class provides an implementation of the {@link FunctionTag} interface for
* tags that are not yet ready to be inserted into the database. This was created
* to allow tags to be imported from an external file and made available to the user
* through the {@link FunctionTagsComponentProvider} UI without needing to formally
* through the {@link FunctionTagProvider} UI without needing to formally
* add them to the {@code FunctionTagAdapter} table.
*/
class InMemoryFunctionTag implements FunctionTag {

View file

@ -15,44 +15,20 @@
*/
package ghidra.app.plugin.core.function.tags;
import java.util.*;
import java.util.Set;
import ghidra.app.cmd.function.AddFunctionTagCmd;
import ghidra.app.cmd.function.CreateFunctionTagCmd;
import ghidra.framework.cmd.Command;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionTag;
import resources.ResourceManager;
/**
* List for displaying all tags in the program that have NOT yet been assigned
* to a function.
* List for displaying all tags in the programs
*/
public class SourceTagsPanel extends TagListPanel {
/**
* Optional! If there is a file with this name which can be found by the
* {@link ResourceManager}, and it contains a valid list of tag names,
* they will be loaded. The file must be XML with the following
* structure:
*
* <tags>
* <tag>
* <name>TAG1</name>
* <comment>tag comment</comment>
* </tag>
* </tags>
*
*/
private static String TAG_FILE = "functionTags.xml";
// Keeps a list of the original tags as loaded from file. This is necessary when switching
// between programs where we need to know the original state of the disabled tags. Without
// this we would need to reload from file on each new program activation.
private Set<FunctionTag> tagsFromFile;
/**
* Constructor
*
@ -60,12 +36,9 @@ public class SourceTagsPanel extends TagListPanel {
* @param tool the plugin tool
* @param title the title of the panel
*/
public SourceTagsPanel(FunctionTagsComponentProvider provider, PluginTool tool, String title) {
public SourceTagsPanel(FunctionTagProvider provider, PluginTool tool, String title) {
super(provider, tool, title);
// Load any tags from external sources
tagsFromFile = loadTags();
table.setDisabled(true);
}
@ -106,13 +79,7 @@ public class SourceTagsPanel extends TagListPanel {
@Override
protected Set<FunctionTag> backgroundLoadTags() {
List<? extends FunctionTag> dbTags = getAllTagsFromDatabase();
// Add any tags from the file system that are not in the db
Set<FunctionTag> allTags = new HashSet<>(dbTags);
allTags.addAll(tagsFromFile);
return allTags;
return provider.backgroundLoadTags();
}
/**
@ -129,30 +96,4 @@ public class SourceTagsPanel extends TagListPanel {
return true;
}
/******************************************************************************
* PRIVATE METHODS
******************************************************************************/
/**
* Returns an array of all tags stored in the database.
*
* @return list of tags
*/
private List<? extends FunctionTag> getAllTagsFromDatabase() {
if (program == null) {
return Collections.emptyList();
}
FunctionManagerDB functionManagerDB = (FunctionManagerDB) program.getFunctionManager();
return functionManagerDB.getFunctionTagManager().getAllFunctionTags();
}
/**
* Loads tags from the external file specified.
*
* @return the loaded tags
*/
private Set<FunctionTag> loadTags() {
return FunctionTagLoader.loadTags(TAG_FILE);
}
}

View file

@ -45,6 +45,7 @@ import ghidra.util.table.GhidraThreadedTablePanel;
public abstract class TagListPanel extends JPanel {
protected PluginTool tool;
protected FunctionTagProvider provider;
protected Program program;
protected Function function;
@ -61,13 +62,13 @@ public abstract class TagListPanel extends JPanel {
* @param tool the plugin tool
* @param title the title of the panel
*/
public TagListPanel(FunctionTagsComponentProvider provider, PluginTool tool, String title) {
public TagListPanel(FunctionTagProvider provider, PluginTool tool, String title) {
this.tool = tool;
this.provider = provider;
setLayout(new BorderLayout());
model = new FunctionTagTableModel("Function Tags", provider.getTool(),
this::backgroundLoadTags);
model = new FunctionTagTableModel("Function Tags", provider.getTool(), this);
GhidraThreadedTablePanel<FunctionTagRowObject> tablePanel =
new GhidraThreadedTablePanel<>(model) {
protected GTable createTable(ThreadedTableModel<FunctionTagRowObject, ?> tm) {
@ -121,10 +122,6 @@ public abstract class TagListPanel extends JPanel {
*/
public abstract void refresh(Function newFunction);
/**
* Called by a background thread to load the tags for this panel
* @return the tags
*/
protected abstract Set<FunctionTag> backgroundLoadTags();
void editRow(int row) {
@ -165,11 +162,11 @@ public abstract class TagListPanel extends JPanel {
});
DockingWindowManager.showDialog(tool.getActiveWindow(), dialog);
if (dialog.isCanceled()) {
return;
String[] results = dialog.getValues();
if (results[0] == null) {
return; // cancelled/closed
}
String[] results = dialog.getValues();
String newName = results[0].trim();
String newComment = results[1].trim();
@ -192,7 +189,7 @@ public abstract class TagListPanel extends JPanel {
return model;
}
FunctionTagTable getTable() {
public FunctionTagTable getTable() {
return table;
}

View file

@ -24,8 +24,7 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionTag;
/**
* Displays a list of tags that have been assigned to the currently-selected
* function in the listing
* Displays a list of tags that have been assigned to the current function
*/
public class TargetTagsPanel extends TagListPanel {
@ -36,7 +35,7 @@ public class TargetTagsPanel extends TagListPanel {
* @param tool the plugin tool
* @param title the panel title
*/
public TargetTagsPanel(FunctionTagsComponentProvider provider,
public TargetTagsPanel(FunctionTagProvider provider,
PluginTool tool, String title) {
super(provider, tool, title);

View file

@ -23,18 +23,19 @@ import java.util.*;
import javax.swing.AbstractButton;
import org.apache.commons.lang3.StringUtils;
import org.junit.*;
import docking.ActionContext;
import docking.action.DockingActionIf;
import docking.widgets.dialogs.InputDialog;
import docking.widgets.textfield.HintTextField;
import ghidra.app.cmd.function.DeleteFunctionCmd;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.services.ProgramManager;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.function.FunctionDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramLocation;
@ -43,13 +44,13 @@ import ghidra.util.exception.UsrException;
/**
* Test class for the {@link FunctionTagPlugin}. This tests the ability to use the
* function tag edit GUI ({@link FunctionTagsComponentProvider} to create/edit/delete
* function tag edit GUI ({@link FunctionTagProvider} to create/edit/delete
* tags.
*
* Test related to the merging and diffing of tags are defined in the
* {@link FunctionTagMergeTest} class.
*/
public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
public class FunctionTagPluginTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env;
private PluginTool tool;
@ -57,8 +58,6 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
private CodeBrowserPlugin cb;
private FunctionTagPlugin plugin;
// Menu item available on a function entry point that launches the
// dialog.
private DockingActionIf editFunctionTags;
private Address NON_FUNCTION_ADDRESS;
@ -66,8 +65,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
private Address FUNCTION_ENTRY_ADDRESS_2;
private Address FUNCTION_ENTRY_ADDRESS_3;
// The UI we're testing.
private FunctionTagsComponentProvider provider = null;
private FunctionTagProvider provider = null;
@Before
public void setUp() throws Exception {
@ -104,10 +102,6 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
env.dispose();
}
/****************************************************************************************
* TESTS
****************************************************************************************/
/**
* Tests that the menu option for bringing up the editor exists
* on function headers only.
@ -143,13 +137,13 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testDeleteImmutableTag() throws Exception {
FunctionTagTable table = getTagListInPanel("sourcePanel");
FunctionTagTable table = getSourceTable();
// Get an immutable tag from the source panel and set it to be selected. Verify that
// the delete button is disabled.
InMemoryFunctionTag immutableTag = getImmutableTag();
assertTrue("Must have at least one immutable tag for this test", immutableTag != null);
selectTagInList(immutableTag.getName(), table);
selectTagInTable(immutableTag.getName(), table);
waitForSwing();
assertFalse(isButtonEnabled("deleteBtn"));
@ -164,7 +158,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
// Select just the non-immutable tag and verify that the delete button is now
// enabled.
selectTagInList(tagName, table);
selectTagInTable(tagName, table);
waitForSwing();
assertTrue(isButtonEnabled("deleteBtn"));
}
@ -177,7 +171,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
@Test
public void testDeleteImmutableTagAfterUse() throws Exception {
FunctionTagTable table = getTagListInPanel("targetPanel");
FunctionTagTable table = getTargetTable();
// Get an immutable tag from the source panel.
InMemoryFunctionTag tag = getImmutableTag();
@ -191,7 +185,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
boolean inList = tagExists(tag.getName(), getAllTags());
assertTrue(inList);
selectTagInList(tag.getName(), table);
selectTagInTable(tag.getName(), table);
waitForSwing();
assertTrue(isButtonEnabled("deleteBtn"));
}
@ -292,8 +286,22 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
String name = "TEST";
createTag(name);
addTagToFunction(name, FUNCTION_ENTRY_ADDRESS);
assertTableTagCount(name, 1);
removeTagFromFunction(name, FUNCTION_ENTRY_ADDRESS);
assertFalse(tagExists(name, getTags(FUNCTION_ENTRY_ADDRESS)));
assertTableTagCount(name, 0);
}
@Test
public void testFunctionRemoved() throws Exception {
String name = "TEST";
createTag(name);
addTagToFunction(name, FUNCTION_ENTRY_ADDRESS);
assertTableTagCount(name, 1);
removeFunction(FUNCTION_ENTRY_ADDRESS);
assertTrue(tagExists(name, getAllTags()));
assertTableTagCount(name, 0);
}
/**
@ -305,7 +313,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
public void testViewFunctionsForTag() throws Exception {
// Verify that the function panel is initially empty
AllFunctionsPanel functionsPanel = getFunctionsPanel();
AllFunctionsPanel functionsPanel = provider.getAllFunctionsPanel();
List<Function> functions = functionsPanel.getFunctions();
assertTrue(functions.isEmpty());
@ -315,8 +323,8 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
addTagToFunction(tagName1, FUNCTION_ENTRY_ADDRESS);
// Select the tag in the target panel
FunctionTagTable table = getTagListInPanel("targetPanel");
selectTagInList(tagName1, table);
FunctionTagTable table = getTargetTable();
selectTagInTable(tagName1, table);
waitForTableModel(functionsPanel.getTableModel());
// Verify that the function is shown in the function panel (check that
@ -338,7 +346,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
public void testMultipleFunctionsWithTag() throws Exception {
// Verify that the function panel is initially empty
AllFunctionsPanel functionsPanel = getFunctionsPanel();
AllFunctionsPanel functionsPanel = provider.getAllFunctionsPanel();
List<Function> functions = functionsPanel.getFunctions();
assertTrue(functions.isEmpty());
@ -349,8 +357,8 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
addTagToFunction(tagName1, FUNCTION_ENTRY_ADDRESS_2);
// Select the tag in the target panel
FunctionTagTable table = getTagListInPanel("targetPanel");
selectTagInList(tagName1, table);
FunctionTagTable table = getTargetTable();
selectTagInTable(tagName1, table);
waitForTableModel(functionsPanel.getTableModel());
// Verify that both functions are shown in the function panel (check that
@ -374,7 +382,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
public void testViewMultipleFunctions() throws Exception {
// Verify that the function panel is initially empty
AllFunctionsPanel functionsPanel = getFunctionsPanel();
AllFunctionsPanel functionsPanel = provider.getAllFunctionsPanel();
List<Function> functions = functionsPanel.getFunctions();
assertTrue(functions.isEmpty());
@ -391,8 +399,8 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
// Select both tags and verify that 3 functions are listed in
// the functions panel
goTo(tool, program, addr("00000000"));
FunctionTagTable table = getTagListInPanel("sourcePanel");
selectTagInList(tagName1, table);
FunctionTagTable table = getSourceTable();
selectTagInTable(tagName1, table);
int index = table.getSelectedRow();
table.addRowSelectionInterval(index, index + 1);
clickTableRange(table, index, 2);
@ -466,9 +474,9 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
cb.goTo(new ProgramLocation(program, address));
waitForSwing();
FunctionTagTable list = getTagListInPanel("sourcePanel");
selectTagInList(name, list);
clickButtonByName("addBtn");
FunctionTagTable list = getSourceTable();
selectTagInTable(name, list);
clickButton("addBtn");
}
/**
@ -483,42 +491,21 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
cb.goTo(new ProgramLocation(program, address));
waitForSwing();
FunctionTagTable table = getTagListInPanel("targetPanel");
selectTagInList(name, table);
clickButtonByName("removeBtn");
FunctionTagTable table = getTargetTable();
selectTagInTable(name, table);
clickButton("removeBtn");
}
/**
* Gets the instance of the tag table in the given panel.
*
* @param panelName the name of the panel to search
* @return the function tag list
* @throws UsrException if there's an error retrieving the panel or tag list instances
*/
private FunctionTagTable getTagListInPanel(String panelName) throws UsrException {
Object comp = getInstanceField(panelName, provider);
if (comp == null) {
throw new UsrException("Error getting targetPanel field in provider");
}
Object table = getInstanceField("table", comp);
if (table == null) {
throw new UsrException("Error getting table in tag panel");
}
FunctionTagTable tagTable = (FunctionTagTable) table;
return tagTable;
private void removeFunction(Address address) {
applyCmd(program, new DeleteFunctionCmd(address));
}
/**
* Returns the functions panel
*
* @return the functions panel
*/
private AllFunctionsPanel getFunctionsPanel() {
AllFunctionsPanel panel =
(AllFunctionsPanel) getInstanceField("allFunctionsPanel", provider);
return panel;
private FunctionTagTable getTargetTable() {
return provider.getTargetPanel().getTable();
}
private FunctionTagTable getSourceTable() {
return provider.getSourcePanel().getTable();
}
/**
@ -528,7 +515,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
* @param table the table to search
* @throws Exception if there is a problem clicking cells in a list
*/
private void selectTagInList(String name, FunctionTagTable table) throws Exception {
private void selectTagInTable(String name, FunctionTagTable table) throws Exception {
assertTagExists(name, table);
int row = 0;
@ -548,14 +535,9 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
* Clicks the button with the given name in the {@link FunctionTagButtonPanel}.
*
* @param name the button name
* @throws UsrException if there's an error retrieving the button panel instance
*/
private void clickButtonByName(String name) throws UsrException {
Object buttonPanel = getInstanceField("buttonPanel", provider);
if (buttonPanel == null) {
throw new UsrException("Error getting button panel field in provider");
}
FunctionTagButtonPanel btnPanel = (FunctionTagButtonPanel) buttonPanel;
private void clickButton(String name) {
FunctionTagButtonPanel btnPanel = provider.getButtonPanel();
pressButtonByName(btnPanel, name);
waitForSwing();
}
@ -565,14 +547,9 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
*
* @param name the button name
* @return true if enabled
* @throws UsrException if there's an error retrieving the button panel instance
*/
private boolean isButtonEnabled(String name) throws UsrException {
Object buttonPanel = getInstanceField("buttonPanel", provider);
if (buttonPanel == null) {
throw new UsrException("Error getting button panel field in provider");
}
FunctionTagButtonPanel btnPanel = (FunctionTagButtonPanel) buttonPanel;
private boolean isButtonEnabled(String name) {
FunctionTagButtonPanel btnPanel = provider.getButtonPanel();
AbstractButton button = findAbstractButtonByName(btnPanel, name);
return isEnabled(button);
}
@ -591,11 +568,17 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
return null;
}
/**
* Loads the notepad program.
*
* @throws Exception if there's an error creating the program builder
*/
private void assertTableTagCount(String name, int expected) {
waitForSwing();
FunctionTagTableModel model = provider.getSourcePanel().getModel();
waitForTableModel(model);
FunctionTagRowObject rowObject = model.getRowObject(name);
assertNotNull(rowObject);
assertEquals("Tag count in table is incorrect", expected, rowObject.getCount());
}
private void loadProgram() throws Exception {
ClassicSampleX86ProgramBuilder builder = new ClassicSampleX86ProgramBuilder();
@ -604,37 +587,35 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
pm.openProgram(program.getDomainFile());
}
/**
* Displays the function tag dialog.
*/
private void showDialog() {
performAction(editFunctionTags, cb.getProvider(), false);
provider = waitForComponentProvider(FunctionTagsComponentProvider.class);
provider = waitForComponentProvider(FunctionTagProvider.class);
tool.showComponentProvider(provider, true);
}
/**
* Places the code browser cursor at the given function entry point.
*
* @param address entry point of a function
*/
private void goToFunction(Address address) {
cb.goTo(new ProgramLocation(program, address));
assertEquals(address, cb.getCurrentAddress());
}
/**
* Adds a tag to the database.
*
* @param name the name of the tag
*/
private void createTag(String name) {
private void createTag(String nameList) {
HintTextField inputField = provider.getTagInputField();
setText(inputField, name);
setText(inputField, nameList);
triggerEnter(inputField);
waitForTasks();
waitForTables();
waitForSwing();
Collection<? extends FunctionTag> tags = getAllTags();
String[] parts = nameList.split(",");
for (String name : parts) {
if (StringUtils.isBlank(name)) {
continue;
}
name = name.trim();
assertTrue("Tag not created '" + name + "' from text '" + nameList + "'",
tagExists(name.trim(), tags));
}
}
private void waitForTables() {
@ -644,20 +625,14 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
FunctionTagTableModel smodel = sourcePanel.getModel();
waitForTableModel(tmodel);
waitForTableModel(smodel);
waitForSwing();
}
/**
* Removes a tag from the database.
*
* @param name the name of the tag
* @throws IOException if there's an error retrieving tags from the database
*/
private void deleteTag(String name) throws IOException {
private void deleteTag(String name) {
FunctionTag tag = getTagForName(name, getAllTags());
tx(program, () -> tag.delete());
waitForTables();
waitForSwing();
}
/**
@ -691,7 +666,7 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
*/
private InMemoryFunctionTag getImmutableTag() throws UsrException {
FunctionTagTable table = getTagListInPanel("sourcePanel");
FunctionTagTable table = getSourceTable();
FunctionTagTableModel model = (FunctionTagTableModel) table.getModel();
Optional<FunctionTagRowObject> optional =
model.getModelData().stream().filter(row -> row.isImmutable()).findAny();
@ -700,35 +675,15 @@ public class FunctionTagEditorTest extends AbstractGhidraHeadedIntegrationTest {
return (InMemoryFunctionTag) foundTag;
}
/**
* Returns all tags in the database.
*
* @return list of all tags in the database
*/
private Collection<? extends FunctionTag> getAllTags() {
FunctionManagerDB functionManager = (FunctionManagerDB) program.getFunctionManager();
return functionManager.getFunctionTagManager().getAllFunctionTags();
return provider.backgroundLoadTags();
}
/**
* Returns true if a tag is in the given list.
*
* @param name the tag name to search for
* @param tags the list to inspect
* @return true if found
*/
private boolean tagExists(String name, Collection<? extends FunctionTag> tags) {
FunctionTag tag = getTagForName(name, tags);
return tag != null;
}
/**
* Returns the {@link FunctionTag} object with the given name.
*
* @param name the tag name
* @param tags the list to inspect
* @return function tag, or null if not found
*/
private FunctionTag getTagForName(String name, Collection<? extends FunctionTag> tags) {
for (FunctionTag tag : tags) {
if (tag.getName().equals(name)) {

View file

@ -56,7 +56,7 @@ public class HintTextField extends JTextField {
*
* @param hint the hint text
* @param required true if the field should be marked as required
*/
*/
public HintTextField(String hint, boolean required) {
this(hint, required, null);
}
@ -81,14 +81,14 @@ public class HintTextField extends JTextField {
* Key listener allows us to check field validity on every key typed
*/
public void addListeners() {
getDocument().addDocumentListener( new DocumentListener() {
getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
validateField();
}
@Override
public void insertUpdate(DocumentEvent e) {
validateField();
@ -119,18 +119,20 @@ public class HintTextField extends JTextField {
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (getText().isEmpty()) {
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.lightGray);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (hint != null) {
g2.drawString(hint, 7, 19);
}
}
if (!getText().isEmpty() || hint == null) {
return;
}
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.LIGHT_GRAY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Rectangle bounds = getBounds();
int x = 10; // offset
int y = bounds.height - bounds.y; // baseline of text; bottom of the text field
g2.drawString(hint, x, y);
}
/**

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +15,7 @@
*/
package generic.concurrent;
import java.util.Iterator;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
@ -26,7 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentListenerSet<T> implements Iterable<T> {
// we use a ConcurrentHashMap because Java has no ConcurrentHashSet
private ConcurrentHashMap<T, T> storage = new ConcurrentHashMap<T, T>();
private ConcurrentHashMap<T, T> storage = new ConcurrentHashMap<>();
public void add(T t) {
storage.put(t, t);
@ -44,4 +43,8 @@ public class ConcurrentListenerSet<T> implements Iterable<T> {
public Iterator<T> iterator() {
return storage.keySet().iterator();
}
public List<T> asList() {
return new ArrayList<>(storage.keySet());
}
}

View file

@ -283,8 +283,9 @@ public class FunctionDB extends DatabaseObject implements Function {
manager.lock.acquire();
try {
checkIsValid();
return manager.getCodeManager().getComment(CodeUnit.REPEATABLE_COMMENT,
getEntryPoint());
return manager.getCodeManager()
.getComment(CodeUnit.REPEATABLE_COMMENT,
getEntryPoint());
}
finally {
manager.lock.release();
@ -301,8 +302,9 @@ public class FunctionDB extends DatabaseObject implements Function {
manager.lock.acquire();
try {
checkDeleted();
manager.getCodeManager().setComment(getEntryPoint(), CodeUnit.REPEATABLE_COMMENT,
comment);
manager.getCodeManager()
.setComment(getEntryPoint(), CodeUnit.REPEATABLE_COMMENT,
comment);
}
finally {
manager.lock.release();
@ -885,8 +887,9 @@ public class FunctionDB extends DatabaseObject implements Function {
}
}
}
program.getBookmarkManager().setBookmark(getEntryPoint(), BookmarkType.ERROR,
"Bad Variables Removed", "Removed " + badSymbols.size() + " bad variables");
program.getBookmarkManager()
.setBookmark(getEntryPoint(), BookmarkType.ERROR,
"Bad Variables Removed", "Removed " + badSymbols.size() + " bad variables");
for (Symbol s : badSymbols) {
s.delete();
}
@ -2703,8 +2706,10 @@ public class FunctionDB extends DatabaseObject implements Function {
callFixupMap.remove(entryPoint);
}
else {
if (program.getCompilerSpec().getPcodeInjectLibrary().getPayload(
InjectPayload.CALLFIXUP_TYPE, name, null, null) == null) {
if (program.getCompilerSpec()
.getPcodeInjectLibrary()
.getPayload(
InjectPayload.CALLFIXUP_TYPE, name, null, null) == null) {
Msg.warn(this, "Undefined CallFixup set at " + entryPoint + ": " + name);
}
callFixupMap.add(entryPoint, name);
@ -2815,13 +2820,12 @@ public class FunctionDB extends DatabaseObject implements Function {
tag = tagManager.createFunctionTag(name, "");
}
FunctionTagMappingAdapter mappingAdapter = tagManager.getFunctionTagMappingAdapter();
if (mappingAdapter.getRecord(getID(), tag.getId()) == null) {
mappingAdapter.createFunctionTagRecord(getID(), tag.getId());
if (!tagManager.isTagApplied(getID(), tag.getId())) {
tagManager.applyFunctionTag(getID(), tag.getId());
Address addr = getEntryPoint();
program.setChanged(ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION, addr, addr, null,
null);
program.setChanged(ChangeManager.DOCR_TAG_ADDED_TO_FUNCTION, addr, addr, tag,
tag);
}
// Add to local cache
@ -2829,9 +2833,6 @@ public class FunctionDB extends DatabaseObject implements Function {
tags.add(tag);
}
}
catch (IOException e) {
manager.dbError(e);
}
finally {
endUpdate();
manager.lock.release();
@ -2853,13 +2854,12 @@ public class FunctionDB extends DatabaseObject implements Function {
FunctionTagManagerDB tagManager =
(FunctionTagManagerDB) manager.getFunctionTagManager();
FunctionTagMappingAdapter mappingAdapter = tagManager.getFunctionTagMappingAdapter();
boolean removed = mappingAdapter.removeFunctionTagRecord(getID(), tag.getId());
boolean removed = tagManager.removeFunctionTag(getID(), tag.getId());
if (removed) {
Address addr = getEntryPoint();
program.setChanged(ChangeManager.DOCR_TAG_REMOVED_FROM_FUNCTION, addr, addr, null,
null);
program.setChanged(ChangeManager.DOCR_TAG_REMOVED_FROM_FUNCTION, addr, addr, tag,
tag);
// Remove from the local cache.
if (tags != null) {
@ -2867,9 +2867,6 @@ public class FunctionDB extends DatabaseObject implements Function {
}
}
}
catch (IOException e) {
manager.dbError(e);
}
finally {
endUpdate();
manager.lock.release();

View file

@ -21,7 +21,8 @@ import java.util.function.Predicate;
import db.*;
import generic.FilteredIterator;
import ghidra.program.database.*;
import ghidra.program.database.DBObjectCache;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.code.CodeManager;
import ghidra.program.database.external.ExternalLocationDB;
import ghidra.program.database.map.AddressMap;
@ -48,7 +49,7 @@ import ghidra.util.task.TaskMonitor;
* all function related calls are routed to this class.
*
*/
public class FunctionManagerDB implements ManagerDB, FunctionManager {
public class FunctionManagerDB implements FunctionManager {
private final String CALLFIXUP_MAP = "CallFixup"; // string map used to store call-fixup name
@ -64,7 +65,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
private NamespaceManager namespaceMgr;
private SymbolManager symbolMgr;
private CodeManager codeMgr;
private FunctionTagManager functionTagManager;
private FunctionTagManagerDB functionTagManager;
private Namespace globalNamespace;
private Predicate<Function> functionFilter = f -> {
@ -352,7 +353,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
throw new IllegalArgumentException(
"Function entryPoint may not be created on defined data");
}
if (namespaceMgr.overlapsNamespace(body) != null) {
throw new OverlappingFunctionException(entryPoint);
}
@ -552,7 +553,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
// Remove all tag mappings associated with this function
for (FunctionTag tag : function.getTags()) {
tag.delete();
function.removeTag(tag.getName());
}
long functionID = function.getID();
@ -930,6 +931,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
public void invalidateCache(boolean all) {
lock.acquire();
try {
functionTagManager.invalidateCache();
callFixupMap = null;
lastFuncID = -1;
cache.invalidate();
@ -996,14 +998,16 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
*/
FunctionIteratorDB(boolean external, boolean forward) {
if (external) {
it = program.getSymbolTable().getSymbols(
new AddressSet(AddressSpace.EXTERNAL_SPACE.getMinAddress(),
AddressSpace.EXTERNAL_SPACE.getMaxAddress()),
SymbolType.FUNCTION, forward);
it = program.getSymbolTable()
.getSymbols(
new AddressSet(AddressSpace.EXTERNAL_SPACE.getMinAddress(),
AddressSpace.EXTERNAL_SPACE.getMaxAddress()),
SymbolType.FUNCTION, forward);
}
else {
it = program.getSymbolTable().getSymbols(program.getMemory(), SymbolType.FUNCTION,
forward);
it = program.getSymbolTable()
.getSymbols(program.getMemory(), SymbolType.FUNCTION,
forward);
}
}
@ -1144,8 +1148,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
list.add(symbol);
}
}
for (int i = 0; i < list.size(); i++) {
Symbol symbol = list.get(i);
for (Symbol symbol : list) {
symbol.delete();
}
}
@ -1477,6 +1480,7 @@ public class FunctionManagerDB implements ManagerDB, FunctionManager {
}
}
@Override
public FunctionTagManager getFunctionTagManager() {
return functionTagManager;
}

View file

@ -53,9 +53,10 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
comment = "";
}
if (!comment.equals(record.getString(FunctionTagAdapter.COMMENT_COL))) {
String oldValue = record.getString(FunctionTagAdapter.COMMENT_COL);
if (!comment.equals(oldValue)) {
record.setString(FunctionTagAdapter.COMMENT_COL, comment);
mgr.updateFunctionTag(this);
mgr.updateFunctionTag(this, oldValue, comment);
}
}
@ -77,9 +78,10 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
name = "";
}
if (!name.equals(record.getString(FunctionTagAdapter.NAME_COL))) {
String oldValue = record.getString(FunctionTagAdapter.NAME_COL);
if (!name.equals(oldValue)) {
record.setString(FunctionTagAdapter.NAME_COL, name);
mgr.updateFunctionTag(this);
mgr.updateFunctionTag(this, oldValue, name);
}
}
catch (IOException e) {
@ -137,7 +139,7 @@ public class FunctionTagDB extends DatabaseObject implements FunctionTag {
// is null, use whatever is in the database.
if (rec == null) {
try {
rec = mgr.getFunctionTagAdapter().getRecord(key);
rec = mgr.getTagRecord(key);
}
catch (IOException e) {
mgr.dbError(e);

View file

@ -18,6 +18,8 @@ package ghidra.program.database.function;
import java.io.IOException;
import java.util.*;
import org.apache.commons.collections4.map.LazyMap;
import db.*;
import db.util.ErrorHandler;
import ghidra.program.database.DBObjectCache;
@ -25,6 +27,7 @@ import ghidra.program.database.ProgramDB;
import ghidra.program.model.listing.*;
import ghidra.program.util.ChangeManager;
import ghidra.util.Lock;
import ghidra.util.datastruct.Counter;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
@ -41,6 +44,8 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
private DBObjectCache<FunctionTagDB> cache;
private Map<FunctionTag, Counter> tagCountCache;
protected final Lock lock;
/**
@ -53,7 +58,7 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
* @throws VersionException if the database is incompatible with the current
* schema
* @throws IOException if there is a problem accessing the database.
* @throws CancelledException
* @throws CancelledException if the program loading is cancelled
*/
FunctionTagManagerDB(DBHandle handle, int openMode, Lock lock, TaskMonitor monitor)
throws VersionException, IOException, CancelledException {
@ -62,10 +67,9 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
functionTagAdapter = FunctionTagAdapter.getAdapter(handle, openMode, monitor);
functionTagMappingAdapter = FunctionTagMappingAdapter.getAdapter(handle, openMode, monitor);
cache = new DBObjectCache<FunctionTagDB>(100);
cache = new DBObjectCache<>(100);
}
@Override
public void setProgram(Program program) {
this.program = (ProgramDB) program;
}
@ -172,21 +176,89 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
return null;
}
void updateFunctionTag(FunctionTagDB tag) throws IOException {
boolean isTagApplied(long functionId, long tagId) {
lock.acquire();
try {
return functionTagMappingAdapter.getRecord(functionId, tagId) != null;
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}
return false;
}
void applyFunctionTag(long functionId, long tagId) {
lock.acquire();
try {
FunctionTag tag = getFunctionTag(tagId);
if (tag == null) {
return; // shouldn't happen
}
functionTagMappingAdapter.createFunctionTagRecord(functionId, tagId);
incrementCountCache(tag);
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}
}
private void incrementCountCache(FunctionTag tag) {
if (tagCountCache != null) {
tagCountCache.get(tag).count++;
}
}
private void decrementCountCache(FunctionTag tag) {
if (tagCountCache != null) {
tagCountCache.get(tag).count--;
}
}
boolean removeFunctionTag(long functionId, long tagId) {
lock.acquire();
try {
FunctionTag tag = getFunctionTag(tagId);
if (tag == null) {
return false; // shouldn't happen
}
if (functionTagMappingAdapter.removeFunctionTagRecord(functionId, tagId)) {
decrementCountCache(tag);
return true;
}
}
catch (IOException e) {
dbError(e);
}
finally {
lock.release();
}
return false;
}
void updateFunctionTag(FunctionTagDB tag, String oldValue, String newValue) throws IOException {
// Update the tag attributes.
functionTagAdapter.updateRecord(tag.getRecord());
// Notify subscribers of the change.
fireTagChangedNotification(ChangeManager.DOCR_FUNCTION_TAG_CHANGED, tag);
fireTagChangedNotification(ChangeManager.DOCR_FUNCTION_TAG_CHANGED, tag, oldValue,
newValue);
invalidateFunctions();
}
/* (non-Javadoc)
* TODO: Be a bit smarter about this - if the cache contains all the tags in
* the db we can just return the cache itself. If not, then go to the
* db to get the remainder.
*/
@Override
public List<? extends FunctionTag> getAllFunctionTags() {
@ -211,27 +283,25 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
return Collections.emptyList();
}
public FunctionTagAdapter getFunctionTagAdapter() {
return functionTagAdapter;
public Record getTagRecord(long id) throws IOException {
return functionTagAdapter.getRecord(id);
}
public FunctionTagMappingAdapter getFunctionTagMappingAdapter() {
return functionTagMappingAdapter;
}
/*****************************************************************************
* PRIVATE METHODS
*****************************************************************************/
//==================================================================================================
// Private Methods
//==================================================================================================
/**
* Sends a notification when a tag has been changed (edited or deleted).
*
* @param type {@link ChangeManager} change type
* @param tag the tag that was changed
* @throws IOException
* @param oldValue the old value
* @param newValue the new value
*/
private void fireTagChangedNotification(int type, FunctionTag tag) throws IOException {
program.tagChanged(tag, type, null, null);
private void fireTagChangedNotification(int type, FunctionTag tag, String oldValue,
String newValue) {
program.tagChanged(tag, type, oldValue, newValue);
}
/**
@ -239,9 +309,8 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
*
* @param type {@link ChangeManager} change type
* @param tag the tag that was created
* @throws IOException
*/
private void fireTagCreatedNotification(int type, FunctionTag tag) throws IOException {
private void fireTagCreatedNotification(int type, FunctionTag tag) {
program.tagCreated(tag, type);
}
@ -250,10 +319,9 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
*
* @param type the type of change
* @param tag the tag that was deleted
* @throws IOException
*/
private void fireTagDeletedNotification(int type, FunctionTag tag) throws IOException {
program.tagChanged(tag, type, null, null);
private void fireTagDeletedNotification(int type, FunctionTag tag) {
program.tagChanged(tag, type, tag, null);
}
/**
@ -275,6 +343,7 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
* Deletes the given function tag.
*
* @param tag the tag to delete
* @throws IOException if there is an issue reading from the db
*/
void doDeleteTag(FunctionTag tag) throws IOException {
@ -304,20 +373,56 @@ public class FunctionTagManagerDB implements FunctionTagManager, ErrorHandler {
* Returns all function tags associated with the given function id.
*
* @param functionId the function id
* @return
* @throws IOException
* @return the tags
* @throws IOException if there is an issue reading from the db
*/
Set<FunctionTag> getFunctionTagsByFunctionID(long functionId) throws IOException {
Set<FunctionTag> tags = new HashSet<>();
RecordIterator functionTagMappingRecords =
RecordIterator functionRecords =
functionTagMappingAdapter.getRecordsByFunctionID(functionId);
while (functionTagMappingRecords.hasNext()) {
Record mappingRecord = functionTagMappingRecords.next();
while (functionRecords.hasNext()) {
Record mappingRecord = functionRecords.next();
Record tagRecord = functionTagAdapter.getRecord(
mappingRecord.getLongValue(FunctionTagMappingAdapter.TAG_ID_COL));
tags.add(getFunctionTagFromCache(tagRecord));
}
return tags;
}
void invalidateCache() {
cache.invalidate();
tagCountCache = null;
}
@Override
public int getUseCount(FunctionTag tag) {
lock.acquire();
try {
if (tagCountCache == null) {
buildTagCountCache();
}
Counter counter = tagCountCache.get(tag);
return counter.count;
}
catch (IOException e) {
dbError(e);
return 0;
}
finally {
lock.release();
}
}
private void buildTagCountCache() throws IOException {
Map<FunctionTag, Counter> map = LazyMap.lazyMap(new HashMap<>(), () -> new Counter());
RecordIterator records = functionTagMappingAdapter.getRecords();
while (records.hasNext()) {
Record mappingRecord = records.next();
long tagId = mappingRecord.getLongValue(FunctionTagMappingAdapter.TAG_ID_COL);
FunctionTag tag = getFunctionTag(tagId);
map.get(tag).count++;
}
tagCountCache = map;
}
}

View file

@ -32,7 +32,7 @@ abstract class FunctionTagMappingAdapter {
static final String TABLE_NAME = "Function Tag Map";
static final int CURRENT_VERSION = 0;
static final int FUNCTION_ID_COL = 0;
static final int TAG_ID_COL = 1;
@ -74,7 +74,7 @@ abstract class FunctionTagMappingAdapter {
*
* @param functionID index into the {@link SymbolTable} table
* @return iterator of database records
* @throws IOException
* @throws IOException if database error occurs
*/
abstract RecordIterator getRecordsByFunctionID(long functionID) throws IOException;
@ -84,17 +84,17 @@ abstract class FunctionTagMappingAdapter {
* @param functionID index into the {@link SymbolTable} table
* @param tagID index into the {@link FunctionTagAdapter} table
* @return null if not found
* @throws IOException
* @throws IOException if database error occurs
*/
abstract Record getRecord(long functionID, long tagID) throws IOException;
/**
* Creates a new record with the given function and tag ID's.
*
* @param functionID
* @param tagID
* @param functionID the function's database id
* @param tagID the FunctionTags database id
* @return newly-created database record
* @throws IOException
* @throws IOException if database error occurs
*/
abstract Record createFunctionTagRecord(long functionID, long tagID)
throws IOException;
@ -106,7 +106,7 @@ abstract class FunctionTagMappingAdapter {
* @param functionID index into the {@link SymbolTable} table
* @param tagID index into the {@link FunctionTagAdapter} table
* @return true if the remove was performed
* @throws IOException
* @throws IOException if database error occurs
*/
abstract boolean removeFunctionTagRecord(long functionID, long tagID)
throws IOException;
@ -116,16 +116,22 @@ abstract class FunctionTagMappingAdapter {
* whenever a tag is being deleted from the system.
*
* @param tagID index into the {@link FunctionTagAdapter} table
* @throws IOException
* @throws IOException if database error occurs
*/
abstract void removeFunctionTagRecord(long tagID) throws IOException;
/**
* Determine if the specified tag ID has been applied to a function
* @param tagId tag ID
* @param id tag ID
* @return true if tag applied to one or more functions
* @throws IOException
* @throws IOException if database error occurs
*/
abstract boolean isTagAssigned(long id) throws IOException;
/**
* Returns a RecordIterator over all the records in this table
* @return a RecordIterator over all the records in this table
* @throws IOException if database error occurs
*/
protected abstract RecordIterator getRecords() throws IOException;
}

View file

@ -65,4 +65,9 @@ class FunctionTagMappingAdapterNoTable extends FunctionTagMappingAdapter {
boolean isTagAssigned(long id) throws IOException {
return false;
}
@Override
protected RecordIterator getRecords() throws IOException {
return new EmptyRecordIterator();
}
}

View file

@ -147,6 +147,14 @@ class FunctionTagMappingAdapterV0 extends FunctionTagMappingAdapter implements D
return table.indexIterator(V0_FUNCTION_ID_COL, value, value, true);
}
@Override
protected RecordIterator getRecords() throws IOException {
if (table == null) {
return new EmptyRecordIterator();
}
return table.iterator();
}
@Override
boolean isTagAssigned(long tagID) throws IOException {
if (table == null) {

View file

@ -29,10 +29,7 @@ import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
/**
* Interface to define methods available on a function. Functions
* have a single entry point.
*
*
* Interface to define methods available on a function. Functions have a single entry point.
*/
public interface Function extends Namespace {
@ -110,8 +107,8 @@ public interface Function extends Namespace {
public void setCallFixup(String name);
/**
* Returns the current call-fixup name set on this instruction or null
* if one has not been set.
* Returns the current call-fixup name set on this instruction or null if one has not been set
* @return the name
*/
public String getCallFixup();
@ -131,6 +128,7 @@ public interface Function extends Namespace {
/**
* Returns the function (same as plate) comment as an array of strings where
* each item in the array is a line of text in the comment.
* @return the comments
*/
public String[] getCommentAsArray();
@ -194,11 +192,15 @@ public interface Function extends Namespace {
/**
* Set the return data-type and storage.
* <p>NOTE: The storage and source are ignored if the function does not have custom storage enabled.
* @param type
* @param storage
*
* <p>NOTE: The storage and source are ignored if the function does not have custom storage
* enabled.
*
* @param type the data type
* @param storage the storage
* @param source source to be combined with the overall signature source.
* @throws InvalidInputException if data type is not a fixed length or storage is improperly sized
* @throws InvalidInputException if data type is not a fixed length or storage is improperly
* sized
*/
public void setReturn(DataType type, VariableStorage storage, SourceType source)
throws InvalidInputException;
@ -216,21 +218,22 @@ public interface Function extends Namespace {
/**
* Get the function's signature.
* <br><br>WARNING! It is important to note that the calling convention may not be properly retained
* by the returned signature object if a non-generic calling convention is used by this function as
* defined by the program's compiler specification.
* <br><br>WARNING! It is important to note that the calling convention may not be properly
* retained by the returned signature object if a non-generic calling convention is used by
* this function as defined by the program's compiler specification.
*
* @param formalSignature if true only original raw types will be retained and
* auto-params discarded (e.g., this, __return_storage_ptr__, etc.) within the returned
* signature. If false, the effective signature will be returned where forced indirect
* and auto-params are reflected in the signature. This option has no affect if the specified
* function has custom storage enabled.
*
* @return the function's signature
*/
public FunctionSignature getSignature(boolean formalSignature);
/**
* Return a string representation of the function signature
*
* @param formalSignature if true only original raw return/parameter types will be retained and
* auto-params discarded (e.g., this, __return_storage_ptr__, etc.) within the returned
* signature. If false, the effective signature will be returned where forced indirect
@ -238,6 +241,7 @@ public interface Function extends Namespace {
* function has custom storage enabled.
* @param includeCallingConvention if true prototype will include call convention
* declaration if known.
* @return the prototype
*/
public String getPrototypeString(boolean formalSignature, boolean includeCallingConvention);
@ -464,6 +468,7 @@ public interface Function extends Namespace {
* @param fromOrdinal from ordinal position using the current numbering
* @param toOrdinal the final position of the specified parameter
* @return parameter which was moved
* @throws InvalidInputException if either ordinal is invalid
* @deprecated The use of this method is discouraged. The function signature should generally be
* adjusted with a single call to {@link #updateFunction(String, Variable, List, FunctionUpdateType, boolean, SourceType)}
*/
@ -520,7 +525,7 @@ public interface Function extends Namespace {
/**
* Returns an array of all local and parameter variables
* @return
* @return the variables
*/
public Variable[] getAllVariables();
@ -533,6 +538,7 @@ public interface Function extends Namespace {
* @return the Variable added to the program.
* @throws DuplicateNameException if another local variable or parameter already
* has that name.
* @throws InvalidInputException if there is an error or conflict when resolving the variable
*/
public Variable addLocalVariable(Variable var, SourceType source)
throws DuplicateNameException, InvalidInputException;
@ -553,14 +559,16 @@ public interface Function extends Namespace {
public void setBody(AddressSetView newBody) throws OverlappingFunctionException;
/**
* Returns true if this function has a variable argument list (VarArgs).
* Returns true if this function has a variable argument list (VarArgs)
* @return true if this function has a variable argument list (VarArgs)
*/
public boolean hasVarArgs();
/**
* Set whether parameters can be passed as a VarArg (variable argument list).
* Set whether parameters can be passed as a VarArg (variable argument list)
*
* @param hasVarArgs true if this function has a variable argument list (ie printf(fmt, ...)).
* @param hasVarArgs true if this function has a variable argument list
* (e.g., printf(fmt, ...)).
*/
public void setVarArgs(boolean hasVarArgs);
@ -595,7 +603,7 @@ public interface Function extends Namespace {
/**
* Set whether or not this function uses custom variable storage
* @param hasCustomVariableStorage
* @param hasCustomVariableStorage true if this function uses custom storage
*/
public void setCustomVariableStorage(boolean hasCustomVariableStorage);

View file

@ -15,22 +15,29 @@
*/
package ghidra.program.model.listing;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.ManagerDB;
import ghidra.program.database.function.OverlappingFunctionException;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.exception.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public interface FunctionManager {
/**
* The manager for functions
*/
public interface FunctionManager extends ManagerDB {
/**
* Returns this manager's program
* @return the program
*/
public Program getProgram();
/**
@ -50,8 +57,8 @@ public interface FunctionManager {
public PrototypeModel getDefaultCallingConvention();
/**
* Gets the prototype model of the calling convention with the specified name in this program.
*
* Gets the prototype model of the calling convention with the specified name in this program
* @param name the calling convention name
* @return the named function calling convention prototype model or null.
*/
public PrototypeModel getCallingConvention(String name);
@ -113,25 +120,28 @@ public interface FunctionManager {
throws OverlappingFunctionException;
/**
* Returns the total number of functions in the program including external functions.
* Returns the total number of functions in the program including external functions
* @return the count
*/
public int getFunctionCount();
/**
* Remove a function defined at entryPoint.
* Remove a function defined at entryPoint
* @param entryPoint the entry point
* @return true if the function was removed
*/
public boolean removeFunction(Address entryPoint);
/**
* Get the function at entryPoint.
*
* @return null if there is no function at entryPoint.
* Get the function at entryPoint
* @param entryPoint the entry point
* @return null if there is no function at entryPoint
*/
public Function getFunctionAt(Address entryPoint);
/**
* Get the function which resides at the specified address or is referenced from the specified
* address.
* Get the function which resides at the specified address or is referenced from the specified
* address
*
* @param address function address or address of pointer to a function.
* @return referenced function or null
@ -147,19 +157,18 @@ public interface FunctionManager {
public Function getFunctionContaining(Address addr);
/**
* Returns an iterator over all non-external functions in address (entry point) order.
*
* Returns an iterator over all non-external functions in address (entry point) order
* @param forward true means to iterate in ascending address order
* @return the iterator
*/
public FunctionIterator getFunctions(boolean forward);
/**
* Get an iterator over non-external functions starting at an address and ordered by entry
* address.
* address
*
* @param start starting address
* @param forward true means to iterate in ascending address order
*
* @return an iterator over functions.
*/
public FunctionIterator getFunctions(Address start, boolean forward);
@ -176,9 +185,10 @@ public interface FunctionManager {
/**
* Returns an iterator over all REAL functions in address (entry point) order (real functions
* have instructions, and aren't stubs).
* have instructions, and aren't stubs)
*
* @param forward true means to iterate in ascending address order
* @return the iterator
*/
public FunctionIterator getFunctionsNoStubs(boolean forward);
@ -220,39 +230,6 @@ public interface FunctionManager {
*/
public boolean isInFunction(Address addr);
/**
* @see ghidra.program.database.ManagerDB#moveAddressRange(ghidra.program.model.address.Address,
* ghidra.program.model.address.Address, long, ghidra.util.task.TaskMonitor)
*/
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
throws CancelledException;
/**
* @see ghidra.program.database.ManagerDB#deleteAddressRange(ghidra.program.model.address.Address,
* ghidra.program.model.address.Address, ghidra.util.task.TaskMonitor)
*/
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException;
/**
* @see ghidra.program.database.ManagerDB#setProgram(ghidra.program.database.ProgramDB)
*/
public void setProgram(ProgramDB program);
/**
* @param currentRevision TODO
* @see ghidra.program.database.ManagerDB#programReady(int, int, ghidra.util.task.TaskMonitor)
*/
public void programReady(int openMode, int currentRevision, TaskMonitor monitor)
throws IOException, CancelledException;
/*
* (non-Javadoc)
*
* @see ghidra.program.database.ManagerDB#invalidateCache(boolean)
*/
public void invalidateCache(boolean all);
/**
* Return an iterator over functions that overlap the given address set.
*
@ -268,10 +245,10 @@ public interface FunctionManager {
* function. While this does not account for the actual instruction flow, it is hopefully
* accurate enough for most situations.
*
* @param instrAddr
* @param storageAddr
* @param instrAddr the instruction address
* @param storageAddr the storage address
* @param size varnode size in bytes (1 is assumed if value &lt;= 0)
* @param isRead
* @param isRead true if the reference is a read reference
* @return referenced variable or null if one not found
*/
public Variable getReferencedVariable(Address instrAddr, Address storageAddr, int size,
@ -284,4 +261,30 @@ public interface FunctionManager {
*/
public Function getFunction(long key);
/**
* Returns the function tag manager
* @return the function tag manager
*/
public FunctionTagManager getFunctionTagManager();
/**
* Clears all data caches
* @param all if false, some managers may not need to update their cache if they can
* tell that its not necessary. If this flag is true, then all managers should clear
* their cache no matter what.
*/
@Override
public void invalidateCache(boolean all); // note: redeclared to not throw an exception
/**
* Move all objects within an address range to a new location
* @param fromAddr the first address of the range to be moved
* @param toAddr the address where to the range is to be moved
* @param length the number of addresses to move
* @param monitor the task monitor to use in any upgrade operations
* @throws CancelledException if the user cancelled the operation via the task monitor
*/
@Override
void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
throws CancelledException; // note: redeclared to not throw an AddressOverflowException
}

View file

@ -18,39 +18,39 @@ package ghidra.program.model.listing;
import java.util.List;
/**
* Interface for managing function tags. Tags are simple objects consisting of
* a name and an optional comment, which can be applied to functions in Ghidra.
* Interface for managing function tags. Tags are simple objects consisting of a name and an
* optional comment, which can be applied to functions.
*
* @see ghidra.program.database.function.FunctionTagAdapter FunctionTagAdapter
* @see ghidra.program.database.function.FunctionTagMappingAdapter FunctionTagMappingAdapter
* See ghidra.program.database.function.FunctionTagAdapter
* See ghidra.program.database.function.FunctionTagMappingAdapter
*/
public interface FunctionTagManager {
/**
* Returns the function tag with the given name.
* Returns the function tag with the given name
*
* @param name the tag name
* @return the function tag, or null if not found
*/
FunctionTag getFunctionTag(String name);
public FunctionTag getFunctionTag(String name);
/**
* Returns the function tag with the given database id.
* Returns the function tag with the given database id
*
* @param id the tags database id
* @return the function tag, or null if not found
*/
FunctionTag getFunctionTag(long id);
public FunctionTag getFunctionTag(long id);
/**
* Returns all function tags in the database.
* Returns all function tags in the database
*
* @return list of function tags
*/
List<? extends FunctionTag> getAllFunctionTags();
public List<? extends FunctionTag> getAllFunctionTags();
/**
* Returns true if the given tag is assigned to a function.
* Returns true if the given tag is assigned to a function
*
* @param name the tag name
* @return true if assigned to a function
@ -65,13 +65,12 @@ public interface FunctionTagManager {
* @param comment the comment associated with the tag (optional)
* @return the new function tag
*/
FunctionTag createFunctionTag(String name, String comment);
public FunctionTag createFunctionTag(String name, String comment);
/**
* Sets the program on this manager to allow the manager to persist changes
* and notify subscribers.
*
* @param program the program
* Returns the number of times the given tag has been applied to a function
* @param tag the tag
* @return the count
*/
void setProgram(Program program);
public int getUseCount(FunctionTag tag);
}

View file

@ -187,4 +187,8 @@ public class FunctionManagerTestDouble implements FunctionManager {
throw new UnsupportedOperationException();
}
@Override
public FunctionTagManager getFunctionTagManager() {
throw new UnsupportedOperationException();
}
}

View file

@ -32,58 +32,47 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionIterator;
import ghidra.program.util.ProgramLocation;
import ghidra.util.Swing;
import ghidra.util.exception.UsrException;
public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
@Test
public void testFullWindow() {
showProvider(FunctionTagsComponentProvider.class);
showProvider(FunctionTagProvider.class);
waitForSwing();
addTableData();
captureIsolatedProvider(FunctionTagsComponentProvider.class, 950, 400);
captureIsolatedProvider(FunctionTagProvider.class, 950, 400);
}
@Test
public void testInputField() {
showProvider(FunctionTagsComponentProvider.class);
showProvider(FunctionTagProvider.class);
waitForSwing();
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
final JPanel inputPanel = (JPanel) getInstanceField("inputPanel", provider);
FunctionTagProvider provider = getProvider(FunctionTagProvider.class);
final JPanel inputPanel = provider.getInputPanel();
captureComponent(inputPanel);
}
@Test
public void testFilterField() {
showProvider(FunctionTagsComponentProvider.class);
waitForSwing();
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
final JPanel filterPanel = (JPanel) getInstanceField("filterPanel", provider);
captureComponent(filterPanel);
}
/**
* For this test the item in row 1 of the available tags table must be an editable
* item. If not, edit the function_tags.xml file to remove all items and re-run this.
*/
@Test
public void testEditTag() {
showProvider(FunctionTagsComponentProvider.class);
showProvider(FunctionTagProvider.class);
waitForSwing();
addTableData();
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
SourceTagsPanel sourcePanel = (SourceTagsPanel) getInstanceField("sourcePanel", provider);
FunctionTagTable table = (FunctionTagTable) getInstanceField("table", sourcePanel);
FunctionTagProvider provider = getProvider(FunctionTagProvider.class);
SourceTagsPanel sourcePanel = provider.getSourcePanel();
FunctionTagTable table = sourcePanel.getTable();
Rectangle bounds = table.getCellRect(7, 0, false); // Cell 7 is an editable item
doubleClick(table, bounds.x, bounds.y);
InputDialog warningDialog = waitForDialogComponent(InputDialog.class);
captureDialog(warningDialog);
InputDialog editDialog = waitForDialogComponent(InputDialog.class);
captureDialog(editDialog);
}
/**
/*
* Captures the warning dialog when trying to delete a tag. Note that this assumes the
* tag in row 1 is NOT read-only. If that's the not the case, modify the function_tags.xml
* file to remove any tags that may be interfering with this.
@ -91,12 +80,12 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
* @throws UsrException
*/
@Test
public void testDeleteWarning() throws UsrException {
showProvider(FunctionTagsComponentProvider.class);
public void testDeleteWarning() {
showProvider(FunctionTagProvider.class);
waitForSwing();
addTableData();
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
FunctionTagProvider provider = getProvider(FunctionTagProvider.class);
SourceTagsPanel sourcePanel = (SourceTagsPanel) getInstanceField("sourcePanel", provider);
FunctionTagTable table = (FunctionTagTable) getInstanceField("table", sourcePanel);
table.setRowSelectionInterval(7, 7);
@ -107,20 +96,20 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
captureDialog(warningDialog);
}
/**
/*
* Captures the read-only warning when trying to edit a tag
*
* @throws UsrException
*/
@Test
public void testEditNotAllowedWarning() throws UsrException {
showProvider(FunctionTagsComponentProvider.class);
public void testEditNotAllowedWarning() {
showProvider(FunctionTagProvider.class);
waitForSwing();
addTableData();
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
SourceTagsPanel sourcePanel = (SourceTagsPanel) getInstanceField("sourcePanel", provider);
FunctionTagTable table = (FunctionTagTable) getInstanceField("table", sourcePanel);
FunctionTagProvider provider = getProvider(FunctionTagProvider.class);
SourceTagsPanel sourcePanel = provider.getSourcePanel();
FunctionTagTable table = sourcePanel.getTable();
doubleClickItem(table, "LIBRARY"); // pick a known read-only tag
OptionDialog warningDialog = waitForDialogComponent(OptionDialog.class);
@ -151,7 +140,7 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
private void addTableData() {
FunctionTagsComponentProvider provider = getProvider(FunctionTagsComponentProvider.class);
FunctionTagProvider provider = getProvider(FunctionTagProvider.class);
Swing.runNow(() -> {
provider.programActivated(program);
@ -171,7 +160,7 @@ public class FunctionTagPluginScreenShots extends GhidraScreenShotGenerator {
*
* @param provider the component provider
*/
private void navigateToFunction(FunctionTagsComponentProvider provider) {
private void navigateToFunction(FunctionTagProvider provider) {
FunctionIterator iter = program.getFunctionManager().getFunctions(true);
while (iter.hasNext()) {
Function func = iter.next();