mirror of
https://github.com/NationalSecurityAgency/ghidra
synced 2024-09-18 01:31:53 +00:00
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:
commit
6648cbb8cb
Binary file not shown.
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 31 KiB |
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)) {
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 <= 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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -187,4 +187,8 @@ public class FunctionManagerTestDouble implements FunctionManager {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionTagManager getFunctionTagManager() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue