mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-2140 - Xrefs - Added an action to delete xrefs from the xrefs table
This commit is contained in:
parent
e804e1a01d
commit
1fc7356080
35 changed files with 1032 additions and 278 deletions
|
@ -133,6 +133,19 @@
|
|||
and a preview of the instruction at that address. Clicking on any row in the table will cause
|
||||
the browser to navigate to that address</P>
|
||||
|
||||
<H3><A NAME="Refresh"></A>Refresh
|
||||
<IMG border="0" src="Icons.REFRESH_ICON" alt=""></H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
This action will refresh the table of references. This button will appear disabled
|
||||
when the data is not stale. However, if Ghidra detects that the data <I>may</I> be
|
||||
stale, then the button will become color filled, as it is here. You may push the
|
||||
button for a refresh in either state.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H3><A NAME="Show_Thunk_Xrefs"></A>Show Thunk Xrefs
|
||||
<IMG border="0" src="images/ThunkFunction.gif" alt=""></H3>
|
||||
|
||||
|
@ -148,6 +161,17 @@
|
|||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><A NAME="Delete_Reference"></A>Delete Reference
|
||||
<IMG border="0" src="Icons.DELETE_ICON" alt=""></H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
This action will delete all selected references from the database. This differs
|
||||
from the <A HREF="help/topics/Search/Query_Results_Dialog.htm#Remove_Items">Remove Items
|
||||
</A> action, which will simply remove items from the table.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A NAME="Keyboard_Controls">Keyboard Controls</H2>
|
||||
|
|
|
@ -126,12 +126,6 @@
|
|||
table. You may also access this feature by right-clicking an item in the table and
|
||||
selecting <B>Make Selection</B>.</P>
|
||||
|
||||
<P align="left"><A name="Highlight"></A>The <IMG alt="" src="images/tag_yellow.png">
|
||||
button toggles the highlighting of the matching references. In this case, the
|
||||
term highlight refers to the background color of the item in the Listing and not
|
||||
a <A HREF="help/topics/SetHighlightPlugin/Highlighting.htm">Program Selection
|
||||
Highlight</A>.
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG alt="" src="help/shared/tip.png">
|
||||
|
@ -145,6 +139,26 @@
|
|||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P align="left"><A name="Highlight"></A>The <IMG alt="" src="images/tag_yellow.png">
|
||||
button toggles the highlighting of the matching references. In this case, the
|
||||
term highlight refers to the background color of the item in the Listing and not
|
||||
a <A HREF="help/topics/SetHighlightPlugin/Highlighting.htm">Program Selection
|
||||
Highlight</A>.
|
||||
</P>
|
||||
|
||||
<P align="left"><A name="Delete_Reference"></A>The <IMG alt="" src="Icons.DELETE_ICON">
|
||||
button deletes the selected reference(s). This will delete the reference from the database.
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P>
|
||||
<IMG alt="" src="help/shared/note.yellow.png">Not all table rows can be deleted. This is
|
||||
because the table shows not just items that have a database reference, but also items
|
||||
that are dynamic references, as well as general uses of an item, such as a data access
|
||||
generated by the Decompiler. The delete action will only allow you to delete references
|
||||
that are in the database.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Search Results</H3>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 17 KiB |
|
@ -164,10 +164,22 @@
|
|||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H4><B>Provided by:</B> <I>Symbol Table</I> plugin</H4>
|
||||
|
||||
<P><BR>
|
||||
<H2><A name="Delete_Reference"></A>Delete Reference<IMG src="Icons.DELETE_ICON"></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>This action will delete all selected references from the database.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P>
|
||||
<BR>
|
||||
<BR>
|
||||
<P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P>
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
<UL>
|
||||
<LI><A href="help/topics/SymbolTablePlugin/symbol_table.htm">Symbol Table</A></LI>
|
||||
</UL>
|
||||
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
@ -164,6 +164,41 @@
|
|||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H2><A name="Make_Selection"></A>Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>You can make a selection that corresponds to the symbol addresses that are selected in the
|
||||
<I>Symbol Table</I>.</P>
|
||||
|
||||
<P>To make a selection:</P>
|
||||
|
||||
<OL>
|
||||
<LI>Select the symbols in the Symbol Table (hold the <Ctrl> key down to add to the
|
||||
selection) to be added to the selection.</LI>
|
||||
|
||||
<LI>
|
||||
Right-mouse-click and select "Make Selection" from the popup menu.
|
||||
|
||||
<UL>
|
||||
<LI>Or, click the <IMG src="Icons.MAKE_SELECTION_ICON"> button in the <I>Symbol
|
||||
Table</I> toolbar.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H2><A name="Navigate_on_Incoming_Location_Changes"></A>
|
||||
Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>When selected, the Symbol Table will select the row in the table that corresponds to the
|
||||
symbol selected in the <A href="help/topics/CodeBrowserPlugin/CodeBrowser.htm">Listing</A>.
|
||||
</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
||||
<H2>Renaming a Symbol</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -199,29 +234,6 @@
|
|||
for more discussion on the use of the edit dialog).
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Make_Selection"></A>Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>You can make a selection that corresponds to the symbol addresses that are selected in the
|
||||
<I>Symbol Table</I>.</P>
|
||||
|
||||
<P>To make a selection:</P>
|
||||
|
||||
<OL>
|
||||
<LI>Select the symbols in the Symbol Table (hold the <Ctrl> key down to add to the
|
||||
selection) to be added to the selection.</LI>
|
||||
|
||||
<LI>
|
||||
Right-mouse-click and select "Make Selection" from the popup menu.
|
||||
|
||||
<UL>
|
||||
<LI>Or, click the <IMG src="Icons.MAKE_SELECTION_ICON"> button in the <I>Symbol
|
||||
Table</I> toolbar.</LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2><A name="Pinning a Symbol"></A>Pinning a Symbol</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
@ -250,18 +262,16 @@
|
|||
</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
<H2><A name="Set_Filter"></A>Filtering <IMG src="images/view-filter.png"></H2>
|
||||
<H2><A name="Set_Filter"></A>Filtering</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The list of displayed symbols is determined by the current symbol table settings. These
|
||||
settings can be adjusted by clicking the <I>Filter</I> <IMG src="images/view-filter.png">
|
||||
settings can be adjusted by clicking the <I>Filter</I> <IMG src="Icons.CONFIGURE_FILTER_ICON">
|
||||
button in the toolbar of the <I>Symbol Table</I> window or from the right-mouse popup menu..
|
||||
The displayed symbols will correspond to the selected checkboxes in the <I>Symbol Table
|
||||
Filter</I> dialog.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P align="center"><IMG src="images/view-filter.png"></P>
|
||||
|
||||
<P align="center"><I>Symbol Table Filter</I> Dialog</P>
|
||||
|
||||
<P align="center"><IMG src="images/Filter.png"></P>
|
||||
|
@ -606,7 +616,6 @@
|
|||
</BLOCKQUOTE>
|
||||
|
||||
<P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P>
|
||||
|
||||
<P class="relatedtopic">Related Topics</P>
|
||||
|
||||
<UL>
|
||||
|
|
|
@ -454,7 +454,9 @@ public class CallTreeProvider extends ComponentProviderAdapter {
|
|||
// navigate incoming nodes on selection
|
||||
//
|
||||
navigateIncomingAction =
|
||||
new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName()) {
|
||||
new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName(),
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
// handled later as we receive events
|
||||
|
|
|
@ -336,9 +336,11 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A'
|
||||
tool.addLocalAction(this, action);
|
||||
|
||||
toggleNavigateAction = new ToggleActionBuilder("Memory Map Navigation", plugin.getName())
|
||||
toggleNavigateAction =
|
||||
new ToggleActionBuilder("Navigate on Incoming Location Changes", plugin.getName())
|
||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||
.selected(false)
|
||||
.sharedKeyBinding()
|
||||
.helpLocation(new HelpLocation("MemoryMapPlugin", "Navigation"))
|
||||
.description(HTMLUtilities.toHTML("Toggle <b>on</b> means to select the block" +
|
||||
" that contains the current location"))
|
||||
|
|
|
@ -20,6 +20,7 @@ import static ghidra.app.plugin.core.navigation.locationreferences.LocationRefer
|
|||
import java.util.Objects;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.symbol.DynamicReference;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
|
@ -36,6 +37,11 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
private final LocationReferenceContext context;
|
||||
private final ProgramLocation location;
|
||||
|
||||
/**
|
||||
* Optional reference object. Some clients do not have actual references.
|
||||
*/
|
||||
private Reference reference;
|
||||
|
||||
private int hashCode = -1;
|
||||
|
||||
private static String getRefType(Reference r) {
|
||||
|
@ -57,6 +63,7 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
LocationReference(Reference reference, boolean isOffcutReference) {
|
||||
this(reference.getFromAddress(), null, getRefType(reference), EMPTY_CONTEXT,
|
||||
isOffcutReference);
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
LocationReference(Address locationOfUseAddress, String refType, boolean isOffcutReference) {
|
||||
|
@ -133,6 +140,28 @@ public class LocationReference implements Comparable<LocationReference> {
|
|||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reference that this class is using. This may be null if there is no database
|
||||
* reference associated with this object.
|
||||
* @return the reference; may be null
|
||||
*/
|
||||
public Reference getReference() {
|
||||
return reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class has a {@link Reference} and that reference is not dynamic (i.e.,
|
||||
* the reference exists in the database).
|
||||
*
|
||||
* @return true if this class has a removable reference
|
||||
*/
|
||||
public boolean isDeletable() {
|
||||
if (reference == null) {
|
||||
return false;
|
||||
}
|
||||
return !(reference instanceof DynamicReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode != -1) {
|
||||
|
|
|
@ -18,7 +18,7 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.ListSelectionModel;
|
||||
|
@ -88,6 +88,26 @@ public class LocationReferencesPanel extends JPanel {
|
|||
tableModel.fullReload();
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
return tableModel.isBusy();
|
||||
}
|
||||
|
||||
void remove(List<LocationReference> refs) {
|
||||
for (LocationReference lr : refs) {
|
||||
tableModel.removeObject(lr);
|
||||
}
|
||||
}
|
||||
|
||||
List<LocationReference> getSelectedReferences() {
|
||||
List<LocationReference> list = new ArrayList<>();
|
||||
int[] rows = table.getSelectedRows();
|
||||
for (int row : rows) {
|
||||
LocationReference lr = tableModel.getRowObject(row);
|
||||
list.add(lr);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void addTableModelListener(TableModelListener listener) {
|
||||
tableModel.addTableModelListener(listener);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.DockingAction;
|
||||
|
@ -133,7 +134,7 @@ public class LocationReferencesPlugin extends Plugin
|
|||
return; // not sure if this can happen
|
||||
}
|
||||
|
||||
Set<Reference> refs = XReferenceUtils.getAllXrefs(location);
|
||||
Supplier<Collection<Reference>> refs = () -> XReferenceUtils.getAllXrefs(location);
|
||||
XReferenceUtils.showXrefs(lac.getNavigatable(), tool, service, location, refs);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,18 +18,21 @@ package ghidra.app.plugin.core.navigation.locationreferences;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DefaultActionContext;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.widgets.table.GTable;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.nav.NavigatableRemovalListener;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.model.DomainObjectChangedEvent;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
|
@ -37,6 +40,7 @@ import ghidra.framework.plugintool.PluginTool;
|
|||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
@ -97,8 +101,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
program.addListener(this);
|
||||
|
||||
setTitle(TITLE_PREFIX_REFERENCES);
|
||||
setHelpLocation(
|
||||
new HelpLocation(locationReferencesPlugin.getName(), "LocationReferencesPlugin"));
|
||||
setHelpLocation(new HelpLocation(getOwner(), "LocationReferencesPlugin"));
|
||||
setWindowMenuGroup("References");
|
||||
setTransient();
|
||||
createView();
|
||||
|
@ -235,7 +238,8 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
|
||||
private void createActions() {
|
||||
|
||||
homeAction = new DockingAction("Home", locationReferencesPlugin.getName()) {
|
||||
String toolbarGroup = "A";
|
||||
homeAction = new DockingAction("Home", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
goTo(currentLocationDescriptor.getHomeLocation(),
|
||||
|
@ -243,7 +247,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
}
|
||||
};
|
||||
|
||||
homeAction.setToolBarData(new ToolBarData(HOME_ICON));
|
||||
homeAction.setToolBarData(new ToolBarData(HOME_ICON, toolbarGroup));
|
||||
updateHomeActionState();
|
||||
|
||||
selectionAction =
|
||||
|
@ -256,17 +260,17 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
}
|
||||
};
|
||||
|
||||
highlightAction.setToolBarData(new ToolBarData(HIGHLIGHT_ICON));
|
||||
highlightAction.setToolBarData(new ToolBarData(HIGHLIGHT_ICON, toolbarGroup));
|
||||
highlightAction.setSelected(true);
|
||||
highlightAction.setDescription("Highlight matches in tool");
|
||||
|
||||
refreshAction = new DockingAction("Refresh", locationReferencesPlugin.getName()) {
|
||||
refreshAction = new DockingAction("Refresh", getOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
updateManager.updateNow();
|
||||
}
|
||||
};
|
||||
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON));
|
||||
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, toolbarGroup));
|
||||
refreshAction.setDescription(
|
||||
"<html>Push at any time to refresh the current table of references.<br>" +
|
||||
"This button is highlighted when the data <i>may</i> be stale.<br>");
|
||||
|
@ -274,6 +278,31 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
SelectionNavigationAction selectionNavigationAction =
|
||||
new SelectionNavigationAction(locationReferencesPlugin, referencesPanel.getTable());
|
||||
|
||||
//@formatter:off
|
||||
String actionName = "Delete Reference";
|
||||
DockingAction deleteRefAction = new ActionBuilder(actionName, getOwner())
|
||||
.sharedKeyBinding()
|
||||
.toolBarIcon(Icons.DELETE_ICON)
|
||||
.toolBarGroup(toolbarGroup)
|
||||
.enabledWhen(c -> {
|
||||
if (!(c instanceof LocationReferencesProviderContext lrContext)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (referencesPanel.isBusy()) {
|
||||
return false;
|
||||
}
|
||||
List<LocationReference> refs = lrContext.getDeletableReferences();
|
||||
return !refs.isEmpty();
|
||||
})
|
||||
.onAction(c -> {
|
||||
LocationReferencesProviderContext lrContext = (LocationReferencesProviderContext) c;
|
||||
List<LocationReference> refs = lrContext.getDeletableReferences();
|
||||
deleteRows(refs);
|
||||
})
|
||||
.build();
|
||||
//@formatter:on
|
||||
|
||||
GhidraTable table = referencesPanel.getTable();
|
||||
DockingAction removeItemsAction = new DeleteAction(tool, table);
|
||||
removeItemsAction.setEnabled(false); // off by default; updated when the user clicks the table
|
||||
|
@ -281,12 +310,27 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
tool.addLocalAction(this, refreshAction);
|
||||
tool.addLocalAction(this, selectionAction);
|
||||
tool.addLocalAction(this, highlightAction);
|
||||
tool.addLocalAction(this, deleteRefAction);
|
||||
tool.addLocalAction(this, removeItemsAction);
|
||||
tool.addLocalAction(this, selectionNavigationAction);
|
||||
|
||||
setHelpLocation();
|
||||
}
|
||||
|
||||
private void deleteRows(List<LocationReference> refs) {
|
||||
|
||||
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
|
||||
for (LocationReference lr : refs) {
|
||||
Reference ref = lr.getReference();
|
||||
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
|
||||
compoundCmd.add(cmd);
|
||||
}
|
||||
tool.execute(compoundCmd, program);
|
||||
|
||||
// also remove the object from the table, since they have been deleted
|
||||
referencesPanel.remove(refs);
|
||||
}
|
||||
|
||||
private void updateHomeActionState() {
|
||||
// we have no home location sometimes, like when launched from the service interface when
|
||||
// looking for references to datatypes
|
||||
|
@ -300,13 +344,10 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
}
|
||||
|
||||
private void setHelpLocation() {
|
||||
homeAction.setHelpLocation(new HelpLocation(locationReferencesPlugin.getName(), "Home"));
|
||||
refreshAction.setHelpLocation(
|
||||
new HelpLocation(locationReferencesPlugin.getName(), "Refresh"));
|
||||
selectionAction.setHelpLocation(
|
||||
new HelpLocation(locationReferencesPlugin.getName(), "Select"));
|
||||
highlightAction.setHelpLocation(
|
||||
new HelpLocation(locationReferencesPlugin.getName(), "Highlight"));
|
||||
homeAction.setHelpLocation(new HelpLocation(getOwner(), "Home"));
|
||||
refreshAction.setHelpLocation(new HelpLocation(getOwner(), "Refresh"));
|
||||
selectionAction.setHelpLocation(new HelpLocation(getOwner(), "Select"));
|
||||
highlightAction.setHelpLocation(new HelpLocation(getOwner(), "Highlight"));
|
||||
}
|
||||
|
||||
private void goTo(ProgramLocation loc, Program theProgram) {
|
||||
|
@ -318,7 +359,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
final JTable table = referencesPanel.getTable();
|
||||
table.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
selectionAction.setEnabled(table.getSelectedRowCount() > 0);
|
||||
contextChanged();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -361,6 +402,15 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
GTable getTable() {
|
||||
return referencesPanel.getTable();
|
||||
}
|
||||
|
||||
LocationReferencesPanel getPanel() {
|
||||
return referencesPanel;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Interface methods
|
||||
//==================================================================================================
|
||||
|
@ -403,7 +453,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public ActionContext getActionContext(MouseEvent event) {
|
||||
return new DefaultActionContext(this, referencesPanel.getTable());
|
||||
return new LocationReferencesProviderContext(this);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
@ -413,7 +463,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
|
|||
private class DeleteAction extends DeleteTableRowAction {
|
||||
|
||||
DeleteAction(PluginTool tool, GTable table) {
|
||||
super(table, locationReferencesPlugin.getName());
|
||||
super(table, LocationReferencesProvider.this.getOwner());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DefaultActionContext;
|
||||
|
||||
/**
|
||||
* An {@link ActionContext} for the {@link LocationReferencesProvider}.
|
||||
*/
|
||||
public class LocationReferencesProviderContext extends DefaultActionContext {
|
||||
|
||||
private LocationReferencesProvider locationProvider;
|
||||
private List<LocationReference> selectedReferences;
|
||||
|
||||
public LocationReferencesProviderContext(LocationReferencesProvider provider) {
|
||||
super(provider, provider.getTable());
|
||||
this.locationProvider = provider;
|
||||
}
|
||||
|
||||
public List<LocationReference> getSelectedReferences() {
|
||||
if (selectedReferences == null) {
|
||||
LocationReferencesPanel panel = locationProvider.getPanel();
|
||||
selectedReferences = panel.getSelectedReferences();
|
||||
}
|
||||
|
||||
return selectedReferences;
|
||||
}
|
||||
|
||||
public List<LocationReference> getDeletableReferences() {
|
||||
List<LocationReference> results = new ArrayList<>();
|
||||
List<LocationReference> refs = getSelectedReferences();
|
||||
for (LocationReference lr : refs) {
|
||||
if (lr.isDeletable()) {
|
||||
results.add(lr);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -1395,7 +1395,7 @@ public final class ReferenceUtils {
|
|||
return;
|
||||
}
|
||||
|
||||
Address[] thunkAddrs = func.getFunctionThunkAddresses();
|
||||
Address[] thunkAddrs = func.getFunctionThunkAddresses(false);
|
||||
if (thunkAddrs == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComponent;
|
||||
|
@ -23,10 +25,13 @@ import javax.swing.JComponent;
|
|||
import docking.ActionContext;
|
||||
import docking.WindowPosition;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.util.SymbolInspector;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
|
@ -72,7 +77,21 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
|||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
return new ProgramActionContext(this, program);
|
||||
|
||||
List<Reference> selectedReferences = getSelectedReferences();
|
||||
return new ReferenceTableContext(this, selectedReferences);
|
||||
}
|
||||
|
||||
private List<Reference> getSelectedReferences() {
|
||||
|
||||
List<Reference> list = new ArrayList<>();
|
||||
GhidraTable table = getTable();
|
||||
int[] rows = table.getSelectedRows();
|
||||
for (int row : rows) {
|
||||
Reference ref = referenceKeyModel.getRowObject(row);
|
||||
list.add(ref);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void setCurrentSymbol(Symbol symbol) {
|
||||
|
@ -104,6 +123,10 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
Program getProgram() {
|
||||
return referenceKeyModel.getProgram();
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (isVisible()) {
|
||||
referenceKeyModel.reload();
|
||||
|
@ -126,6 +149,17 @@ class ReferenceProvider extends ComponentProviderAdapter {
|
|||
return referencePanel.getTable();
|
||||
}
|
||||
|
||||
void deleteRows(List<Reference> refs) {
|
||||
|
||||
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
|
||||
for (Reference ref : refs) {
|
||||
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
|
||||
compoundCmd.add(cmd);
|
||||
referenceKeyModel.removeObject(ref);
|
||||
}
|
||||
tool.execute(compoundCmd, getProgram());
|
||||
}
|
||||
|
||||
private String generateSubTitle() {
|
||||
return "(" + referenceKeyModel.getDescription() + ")";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
|
||||
public class ReferenceTableContext extends ProgramActionContext {
|
||||
|
||||
private List<Reference> references;
|
||||
|
||||
ReferenceTableContext(ReferenceProvider provider, List<Reference> references) {
|
||||
super(provider, provider.getProgram(), provider.getTable());
|
||||
this.references = references;
|
||||
}
|
||||
|
||||
List<Reference> getSelectedReferences() {
|
||||
return references;
|
||||
}
|
||||
}
|
|
@ -32,7 +32,11 @@ import docking.widgets.table.RowFilterTransformer;
|
|||
import ghidra.app.plugin.core.symtable.AbstractSymbolTableModel.OriginalNameColumn;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.*;
|
||||
|
||||
class SymbolPanel extends JPanel {
|
||||
|
@ -41,9 +45,9 @@ class SymbolPanel extends JPanel {
|
|||
|
||||
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
|
||||
|
||||
private SymbolProvider symProvider;
|
||||
private SymbolTableModel tableModel;
|
||||
private GhidraTable symTable;
|
||||
private SymbolProvider symbolProvider;
|
||||
private SymbolTableModel symbolModel;
|
||||
private GhidraTable gTable;
|
||||
private TableModelListener listener;
|
||||
private FilterDialog filterDialog;
|
||||
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
|
||||
|
@ -51,34 +55,31 @@ class SymbolPanel extends JPanel {
|
|||
|
||||
SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer,
|
||||
PluginTool tool) {
|
||||
|
||||
super(new BorderLayout());
|
||||
|
||||
this.symProvider = provider;
|
||||
this.tableModel = model;
|
||||
this.symbolProvider = provider;
|
||||
this.symbolModel = model;
|
||||
this.threadedTablePanel = new GhidraThreadedTablePanel<>(model);
|
||||
this.listener = e -> symbolProvider.updateTitle();
|
||||
|
||||
threadedTablePanel = new GhidraThreadedTablePanel<>(model);
|
||||
|
||||
this.listener = e -> symProvider.updateTitle();
|
||||
|
||||
symTable = threadedTablePanel.getTable();
|
||||
symTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL);
|
||||
symTable.setRowSelectionAllowed(true);
|
||||
symTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
symTable.getModel().addTableModelListener(listener);
|
||||
symTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
gTable = threadedTablePanel.getTable();
|
||||
gTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL);
|
||||
gTable.setRowSelectionAllowed(true);
|
||||
gTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
gTable.getModel().addTableModelListener(listener);
|
||||
gTable.getSelectionModel().addListSelectionListener(e -> {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
handleTableSelection();
|
||||
tool.contextChanged(symProvider);
|
||||
tool.contextChanged(symbolProvider);
|
||||
}
|
||||
});
|
||||
|
||||
symTable.setAccessibleNamePrefix("Symbol");
|
||||
gTable.setAccessibleNamePrefix("Symbol");
|
||||
|
||||
symTable.installNavigation(tool);
|
||||
gTable.installNavigation(tool);
|
||||
|
||||
for (int i = 0; i < symTable.getColumnCount(); i++) {
|
||||
TableColumn column = symTable.getColumnModel().getColumn(i);
|
||||
for (int i = 0; i < gTable.getColumnCount(); i++) {
|
||||
TableColumn column = gTable.getColumnModel().getColumn(i);
|
||||
column.setCellRenderer(renderer);
|
||||
if (column.getModelIndex() == AbstractSymbolTableModel.LABEL_COL) {
|
||||
column.setCellEditor(new SymbolEditor());
|
||||
|
@ -91,11 +92,11 @@ class SymbolPanel extends JPanel {
|
|||
filterDialog = new FilterDialog(tool);
|
||||
|
||||
// enable dragging symbols out of the symbol table
|
||||
new SymbolTableDragProvider(symTable, model);
|
||||
new SymbolTableDragProvider(gTable, model);
|
||||
}
|
||||
|
||||
private JPanel createFilterFieldPanel() {
|
||||
tableFilterPanel = new GhidraTableFilterPanel<>(symTable, tableModel);
|
||||
tableFilterPanel = new GhidraTableFilterPanel<>(gTable, symbolModel);
|
||||
tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " +
|
||||
"names that start with the given pattern");
|
||||
|
||||
|
@ -120,17 +121,35 @@ class SymbolPanel extends JPanel {
|
|||
return tableFilterPanel;
|
||||
}
|
||||
|
||||
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) {
|
||||
TableColumnModel columnModel = symTable.getColumnModel();
|
||||
if (nameOnly) {
|
||||
return new NameOnlyRowTransformer(tableModel, columnModel);
|
||||
void locationChanged(ProgramLocation location) {
|
||||
|
||||
Program program = location.getProgram();
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
Address address = location.getAddress();
|
||||
Symbol primarySymbol = symbolTable.getPrimarySymbol(address);
|
||||
if (primarySymbol == null) {
|
||||
return;
|
||||
}
|
||||
return new DefaultRowFilterTransformer<>(tableModel, columnModel);
|
||||
|
||||
SymbolRowObject rowObject = new SymbolRowObject(primarySymbol);
|
||||
int index = symbolModel.getRowIndex(rowObject);
|
||||
if (index >= 0) {
|
||||
gTable.selectRow(index);
|
||||
gTable.scrollToSelectedRow();
|
||||
}
|
||||
}
|
||||
|
||||
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) {
|
||||
TableColumnModel columnModel = gTable.getColumnModel();
|
||||
if (nameOnly) {
|
||||
return new NameOnlyRowTransformer(symbolModel, columnModel);
|
||||
}
|
||||
return new DefaultRowFilterTransformer<>(symbolModel, columnModel);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
symTable.getModel().removeTableModelListener(listener);
|
||||
symTable.dispose();
|
||||
gTable.getModel().removeTableModelListener(listener);
|
||||
gTable.dispose();
|
||||
threadedTablePanel.dispose();
|
||||
tableFilterPanel.dispose();
|
||||
filterDialog.dispose();
|
||||
|
@ -140,24 +159,24 @@ class SymbolPanel extends JPanel {
|
|||
if (filterDialog == null) {
|
||||
return;
|
||||
}
|
||||
if (symTable.isEditing()) {
|
||||
symTable.editingCanceled(null);
|
||||
if (gTable.isEditing()) {
|
||||
gTable.editingCanceled(null);
|
||||
}
|
||||
symProvider.setCurrentSymbol(null);
|
||||
symTable.clearSelection();
|
||||
symbolProvider.setCurrentSymbol(null);
|
||||
gTable.clearSelection();
|
||||
|
||||
filterDialog.adjustFilter(symProvider, tableModel);
|
||||
filterDialog.adjustFilter(symbolProvider, symbolModel);
|
||||
}
|
||||
|
||||
SymbolFilter getFilter() {
|
||||
return tableModel.getFilter();
|
||||
return symbolModel.getFilter();
|
||||
}
|
||||
|
||||
void readConfigState(SaveState saveState) {
|
||||
Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME);
|
||||
if (filterElement != null) {
|
||||
filterDialog.restoreFilter(filterElement);
|
||||
tableModel.setFilter(filterDialog.getFilter());
|
||||
symbolModel.setFilter(filterDialog.getFilter());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,26 +186,26 @@ class SymbolPanel extends JPanel {
|
|||
}
|
||||
|
||||
private void handleTableSelection() {
|
||||
int selectedRowCount = symTable.getSelectedRowCount();
|
||||
int selectedRowCount = gTable.getSelectedRowCount();
|
||||
|
||||
if (selectedRowCount == 1) {
|
||||
int selectedRow = symTable.getSelectedRow();
|
||||
Symbol symbol = symProvider.getSymbolForRow(selectedRow);
|
||||
symProvider.setCurrentSymbol(symbol); // null allowed
|
||||
int selectedRow = gTable.getSelectedRow();
|
||||
Symbol symbol = symbolProvider.getSymbolForRow(selectedRow);
|
||||
symbolProvider.setCurrentSymbol(symbol); // null allowed
|
||||
}
|
||||
else {
|
||||
symProvider.setCurrentSymbol(null);
|
||||
symbolProvider.setCurrentSymbol(null);
|
||||
}
|
||||
}
|
||||
|
||||
int getActualSymbolCount() {
|
||||
return symTable.getRowCount();
|
||||
return gTable.getRowCount();
|
||||
}
|
||||
|
||||
List<Symbol> getSelectedSymbols() {
|
||||
List<Symbol> list = new ArrayList<>();
|
||||
int[] rows = symTable.getSelectedRows();
|
||||
for (SymbolRowObject rowObject : tableModel.getRowObjects(rows)) {
|
||||
int[] rows = gTable.getSelectedRows();
|
||||
for (SymbolRowObject rowObject : symbolModel.getRowObjects(rows)) {
|
||||
Symbol s = rowObject.getSymbol();
|
||||
if (s != null) {
|
||||
list.add(s);
|
||||
|
@ -196,7 +215,7 @@ class SymbolPanel extends JPanel {
|
|||
}
|
||||
|
||||
GhidraTable getTable() {
|
||||
return symTable;
|
||||
return gTable;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -33,6 +33,7 @@ import ghidra.framework.options.SaveState;
|
|||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
|
@ -45,6 +46,8 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
private SymbolTableModel symbolKeyModel;
|
||||
private SymbolPanel symbolPanel;
|
||||
|
||||
private boolean followIncomingLocationChanges;
|
||||
|
||||
SymbolProvider(SymbolTablePlugin plugin) {
|
||||
super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class);
|
||||
this.plugin = plugin;
|
||||
|
@ -63,6 +66,19 @@ class SymbolProvider extends ComponentProviderAdapter {
|
|||
addToTool();
|
||||
}
|
||||
|
||||
void setFollowIncomingLocationChanges(boolean b) {
|
||||
followIncomingLocationChanges = b;
|
||||
}
|
||||
|
||||
void locationChanged(ProgramLocation location) {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
if (followIncomingLocationChanges) {
|
||||
symbolPanel.locationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
void updateTitle() {
|
||||
setSubTitle(generateSubTitle());
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.symtable;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import docking.widgets.table.DiscoverableTableUtils;
|
||||
import docking.widgets.table.TableColumnDescriptor;
|
||||
import ghidra.app.services.BlockModelService;
|
||||
|
@ -53,11 +51,10 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
static final int INSTR_REFS_FROM = 1;
|
||||
static final int DATA_REFS_FROM = 2;
|
||||
|
||||
private Symbol currentSymbol;
|
||||
private volatile Symbol currentSymbol;
|
||||
private ReferenceManager refManager;
|
||||
private int showRefMode = REFS_TO;
|
||||
private BlockModelService blockModelService;
|
||||
private boolean isDisposed;
|
||||
|
||||
SymbolReferenceModel(BlockModelService bms, PluginTool tool) {
|
||||
super("Symbol References", tool, null, null);
|
||||
|
@ -93,17 +90,11 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
int count = filteredData.size();
|
||||
description += count + " Reference";
|
||||
if (count != 1) {
|
||||
description += "s";//make plural...
|
||||
description += "s"; // make plural...
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program prog) {
|
||||
if (isDisposed) {
|
||||
|
@ -145,9 +136,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
}
|
||||
|
||||
private void checkRefs(Symbol symbol) {
|
||||
Iterator<Reference> iter = filteredData.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Reference ref = iter.next();
|
||||
for (Reference ref : filteredData) {
|
||||
if (ref.getFromAddress().equals(symbol.getAddress())) {
|
||||
reload();
|
||||
return;
|
||||
|
@ -173,6 +162,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
|
|||
@Override
|
||||
protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
if (currentSymbol == null || getProgram() == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ import static ghidra.program.util.ProgramEvent.*;
|
|||
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
|
@ -44,6 +46,7 @@ import ghidra.program.model.listing.Data;
|
|||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramChangeRecord;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import ghidra.util.table.SelectionNavigationAction;
|
||||
import ghidra.util.table.actions.MakeProgramSelectionAction;
|
||||
|
@ -70,11 +73,14 @@ import resources.Icons;
|
|||
"to show subsets of the symbols.",
|
||||
servicesRequired = { GoToService.class, BlockModelService.class },
|
||||
eventsProduced = { ProgramLocationPluginEvent.class },
|
||||
eventsConsumed = { ProgramActivatedPluginEvent.class }
|
||||
eventsConsumed = { ProgramActivatedPluginEvent.class, ProgramLocationPluginEvent.class }
|
||||
)
|
||||
//@formatter:on
|
||||
public class SymbolTablePlugin extends Plugin {
|
||||
|
||||
private static final String NAVIGATE_ON_INCOMING_EVENT_KEY = "NAVIGATE_ON_INCOMING_EVENT";
|
||||
private static final String NAVIGATE_ON_OUTGOING_EVENT_KEY = "NAVIGATE_ON_OUTGOING_EVENT";
|
||||
|
||||
final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
|
||||
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
|
||||
|
||||
|
@ -85,6 +91,8 @@ public class SymbolTablePlugin extends Plugin {
|
|||
private ToggleDockingAction referencesToAction;
|
||||
private ToggleDockingAction instructionsFromAction;
|
||||
private ToggleDockingAction dataFromAction;
|
||||
private ToggleDockingAction followIncomingAction;
|
||||
private SelectionNavigationAction selectionNavigationAction;
|
||||
|
||||
private SymbolProvider symProvider;
|
||||
private ReferenceProvider refProvider;
|
||||
|
@ -164,11 +172,21 @@ public class SymbolTablePlugin extends Plugin {
|
|||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
symProvider.readConfigState(saveState);
|
||||
|
||||
boolean navigateIncoming = saveState.getBoolean(NAVIGATE_ON_INCOMING_EVENT_KEY, false);
|
||||
boolean navigateOutgoing = saveState.getBoolean(NAVIGATE_ON_OUTGOING_EVENT_KEY, false);
|
||||
followIncomingAction.setSelected(navigateIncoming);
|
||||
selectionNavigationAction.setSelected(navigateOutgoing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
symProvider.writeConfigState(saveState);
|
||||
|
||||
boolean navigateIncoming = followIncomingAction.isSelected();
|
||||
boolean navigateOutgoing = selectionNavigationAction.isSelected();
|
||||
saveState.putBoolean(NAVIGATE_ON_INCOMING_EVENT_KEY, navigateIncoming);
|
||||
saveState.putBoolean(NAVIGATE_ON_OUTGOING_EVENT_KEY, navigateOutgoing);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -194,6 +212,10 @@ public class SymbolTablePlugin extends Plugin {
|
|||
|
||||
tool.contextChanged(symProvider);
|
||||
}
|
||||
else if (event instanceof ProgramLocationPluginEvent ple) {
|
||||
ProgramLocation location = ple.getLocation();
|
||||
symProvider.locationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isBusy() {
|
||||
|
@ -391,8 +413,22 @@ public class SymbolTablePlugin extends Plugin {
|
|||
setFilterAction.setDescription("Configure Symbol Filter");
|
||||
tool.addLocalAction(symProvider, setFilterAction);
|
||||
|
||||
String navGroup = "2";
|
||||
followIncomingAction =
|
||||
new ToggleDockingAction("Navigate on Incoming Location Changes", getName(),
|
||||
KeyBindingType.SHARED) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
symProvider.setFollowIncomingLocationChanges(isSelected());
|
||||
}
|
||||
};
|
||||
followIncomingAction
|
||||
.setToolBarData(new ToolBarData(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, navGroup));
|
||||
tool.addLocalAction(symProvider, followIncomingAction);
|
||||
|
||||
// override the SelectionNavigationAction to handle both tables that this plugin uses
|
||||
DockingAction selectionNavigationAction =
|
||||
selectionNavigationAction =
|
||||
new SelectionNavigationAction(this, symProvider.getTable()) {
|
||||
|
||||
@Override
|
||||
|
@ -401,6 +437,7 @@ public class SymbolTablePlugin extends Plugin {
|
|||
refProvider.getTable().setNavigateOnSelectionEnabled(listen);
|
||||
}
|
||||
};
|
||||
selectionNavigationAction.getToolBarData().setToolBarGroup(navGroup);
|
||||
tool.addLocalAction(symProvider, selectionNavigationAction);
|
||||
|
||||
String pinnedPopupGroup = "2"; // second group
|
||||
|
@ -416,6 +453,7 @@ public class SymbolTablePlugin extends Plugin {
|
|||
}
|
||||
|
||||
private void createRefActions() {
|
||||
String toolbarGroup = "1";
|
||||
referencesToAction = new ToggleDockingAction("References To", getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
|
@ -435,7 +473,7 @@ public class SymbolTablePlugin extends Plugin {
|
|||
referencesToAction.setDescription("References To");
|
||||
referencesToAction.setSelected(true);
|
||||
referencesToAction.setToolBarData(
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.references.to"), null));
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.references.to"), toolbarGroup));
|
||||
|
||||
tool.addLocalAction(refProvider, referencesToAction);
|
||||
|
||||
|
@ -458,7 +496,7 @@ public class SymbolTablePlugin extends Plugin {
|
|||
instructionsFromAction.setDescription("Instructions From");
|
||||
instructionsFromAction.setSelected(false);
|
||||
instructionsFromAction.setToolBarData(
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.instructions.from"), null));
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.instructions.from"), toolbarGroup));
|
||||
|
||||
tool.addLocalAction(refProvider, instructionsFromAction);
|
||||
|
||||
|
@ -481,9 +519,36 @@ public class SymbolTablePlugin extends Plugin {
|
|||
dataFromAction.setDescription("Data From");
|
||||
dataFromAction.setSelected(false);
|
||||
dataFromAction.setToolBarData(
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.data.from"), null));
|
||||
new ToolBarData(new GIcon("icon.plugin.symboltable.data.from"), toolbarGroup));
|
||||
|
||||
tool.addLocalAction(refProvider, dataFromAction);
|
||||
|
||||
//@formatter:off
|
||||
toolbarGroup = "2";
|
||||
String actionName = "Delete Reference";
|
||||
new ActionBuilder(actionName, getName())
|
||||
.sharedKeyBinding()
|
||||
.toolBarIcon(Icons.DELETE_ICON)
|
||||
.toolBarGroup(toolbarGroup)
|
||||
.enabledWhen(c -> {
|
||||
if (!(c instanceof ReferenceTableContext context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (refProvider.isBusy()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Reference> refs = context.getSelectedReferences();
|
||||
return !refs.isEmpty();
|
||||
})
|
||||
.onAction(c -> {
|
||||
ReferenceTableContext context = (ReferenceTableContext) c;
|
||||
List<Reference> refs = context.getSelectedReferences();
|
||||
refProvider.deleteRows(refs);
|
||||
})
|
||||
.buildAndInstallLocal(refProvider);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
// a HACK to make the given action the selected action
|
||||
|
|
|
@ -36,6 +36,7 @@ import ghidra.app.nav.Navigatable;
|
|||
import ghidra.app.nav.NavigatableRemovalListener;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.plugintool.ComponentProviderAdapter;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -64,6 +65,9 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>();
|
||||
private Callback closedCallback = Dummy.callback();
|
||||
|
||||
// optional client listener
|
||||
private DomainObjectListener programListener;
|
||||
|
||||
private Navigatable navigatable;
|
||||
private SelectionNavigationAction selectionNavigationAction;
|
||||
private DockingAction selectAction;
|
||||
|
@ -422,6 +426,15 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void componentHidden() {
|
||||
// Note: this method will get called when this provider is closed. Also, the provider will
|
||||
// be closed if its program is closed.
|
||||
if (programListener != null) {
|
||||
program.removeListener(programListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWindowSubMenuName() {
|
||||
return windowSubMenu;
|
||||
|
@ -456,4 +469,22 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
|
|||
public void setClosedCallback(Callback c) {
|
||||
this.closedCallback = Dummy.ifNull(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a program listener on this provider. This class will add the listener to the program
|
||||
* and maintain a reference to the listener for the life of this provider. This prevents the
|
||||
* listener from getting garbage collected until this provider is disposed.
|
||||
*
|
||||
* @param programListener the listener
|
||||
*/
|
||||
public void setProgramListener(DomainObjectListener programListener) {
|
||||
if (this.programListener != null) {
|
||||
program.removeListener(programListener);
|
||||
}
|
||||
|
||||
this.programListener = programListener;
|
||||
if (programListener != null) {
|
||||
program.addListener(programListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.app.util;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
|
||||
import ghidra.docking.settings.Settings;
|
||||
|
@ -36,8 +37,8 @@ public class FunctionXrefsTableModel extends ReferencesFromTableModel {
|
|||
private Function function;
|
||||
private boolean showAllThunkXrefs;
|
||||
|
||||
public FunctionXrefsTableModel(Function function, Collection<Reference> directRefs, ServiceProvider sp,
|
||||
Program program) {
|
||||
public FunctionXrefsTableModel(Function function, Supplier<Collection<Reference>> directRefs,
|
||||
ServiceProvider sp, Program program) {
|
||||
super(directRefs, sp, program);
|
||||
this.function = function;
|
||||
|
||||
|
|
|
@ -15,12 +15,28 @@
|
|||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import java.util.*;
|
||||
import static ghidra.framework.model.DomainObjectEvent.*;
|
||||
import static ghidra.program.util.ProgramEvent.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JTable;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ToggleActionBuilder;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.OptionDialogBuilder;
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.table.TableComponentProvider;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.cmd.CompoundCmd;
|
||||
import ghidra.framework.model.DomainObjectListener;
|
||||
import ghidra.framework.model.DomainObjectListenerBuilder;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
|
@ -30,16 +46,18 @@ import ghidra.program.util.*;
|
|||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.table.ReferencesFromTableModel;
|
||||
import ghidra.util.table.field.ReferenceEndpoint;
|
||||
import resources.Icons;
|
||||
import resources.ResourceManager;
|
||||
|
||||
public class XReferenceUtils {
|
||||
|
||||
private static final String X_REFS_TO = "XRefs to ";
|
||||
|
||||
// Methods in this class treat -1 as a key to return all references and
|
||||
// not cap the result set.
|
||||
// Methods in this class treat -1 as a key to return all references and not cap the result set.
|
||||
private final static int ALL_REFS = -1;
|
||||
|
||||
private static OptionDialog promptToDeleteXrefsDialog;
|
||||
|
||||
/**
|
||||
* Returns an array containing the first <b><code>max</code></b>
|
||||
* direct xref references to the specified code unit.
|
||||
|
@ -69,13 +87,15 @@ public class XReferenceUtils {
|
|||
}
|
||||
|
||||
// Check for thunk reference
|
||||
Function func = program.getFunctionManager().getFunctionAt(minAddress);
|
||||
if (func != null) {
|
||||
Address[] thunkAddrs = func.getFunctionThunkAddresses();
|
||||
Function function = program.getFunctionManager().getFunctionAt(minAddress);
|
||||
if (function == null) {
|
||||
return xrefs;
|
||||
}
|
||||
|
||||
Address[] thunkAddrs = function.getFunctionThunkAddresses(false);
|
||||
if (thunkAddrs != null) {
|
||||
for (Address thunkAddr : thunkAddrs) {
|
||||
xrefs.add(new ThunkReference(thunkAddr, func.getEntryPoint()));
|
||||
}
|
||||
xrefs.add(new ThunkReference(thunkAddr, function.getEntryPoint()));
|
||||
}
|
||||
}
|
||||
return xrefs;
|
||||
|
@ -203,10 +223,29 @@ public class XReferenceUtils {
|
|||
* @param service the service needed to show the table
|
||||
* @param location the location for which to find references
|
||||
* @param xrefs the xrefs to show
|
||||
* @deprecated use {@link #showXrefs(Navigatable, ServiceProvider, TableService,
|
||||
* ProgramLocation, Supplier)}. That method takes a supplier that can regenerate the current
|
||||
* xrefs for the table.
|
||||
*/
|
||||
@Deprecated(since = "11.5", forRemoval = true)
|
||||
public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
|
||||
TableService service, ProgramLocation location, Collection<Reference> xrefs) {
|
||||
|
||||
showXrefs(navigatable, serviceProvider, service, location, () -> xrefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all xrefs to the given location in a new table.
|
||||
*
|
||||
* @param navigatable the navigatable used for navigation from the table
|
||||
* @param serviceProvider the service provider needed to wire navigation
|
||||
* @param service the service needed to show the table
|
||||
* @param location the location for which to find references
|
||||
* @param xrefs a supplier of the xrefs to show
|
||||
*/
|
||||
public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
|
||||
TableService service, ProgramLocation location, Supplier<Collection<Reference>> xrefs) {
|
||||
|
||||
Address address = location.getAddress();
|
||||
Program program = location.getProgram();
|
||||
FunctionManager fm = program.getFunctionManager();
|
||||
|
@ -223,9 +262,26 @@ public class XReferenceUtils {
|
|||
String title = generateXRefTitle(location);
|
||||
TableComponentProvider<ReferenceEndpoint> provider =
|
||||
service.showTable(title, "Xrefs", model, "Xrefs", navigatable);
|
||||
|
||||
//
|
||||
// Add Actions
|
||||
//
|
||||
provider.installRemoveItemsAction();
|
||||
|
||||
installRefreshAction(provider, model);
|
||||
|
||||
if (function != null) {
|
||||
installShowThunksAction(provider, model);
|
||||
}
|
||||
|
||||
DeleteXrefsAction deleteAction = new DeleteXrefsAction(provider, model, program);
|
||||
provider.addLocalAction(deleteAction);
|
||||
}
|
||||
|
||||
private static void installShowThunksAction(
|
||||
TableComponentProvider<ReferenceEndpoint> provider,
|
||||
ReferencesFromTableModel model) {
|
||||
|
||||
//@formatter:off
|
||||
String actionName = "Show Thunk Xrefs";
|
||||
new ToggleActionBuilder(actionName, provider.getActionOwner())
|
||||
|
@ -238,10 +294,112 @@ public class XReferenceUtils {
|
|||
})
|
||||
.buildAndInstallLocal(provider);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private static void installRefreshAction(TableComponentProvider<ReferenceEndpoint> provider,
|
||||
ReferencesFromTableModel model) {
|
||||
|
||||
Icon REFRESH_NOT_NEEDED_ICON = ResourceManager.getDisabledIcon(Icons.REFRESH_ICON, 60);
|
||||
DockingAction refreshAction = new DockingAction("Refresh", provider.getActionOwner()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
getToolBarData().setIcon(REFRESH_NOT_NEEDED_ICON);
|
||||
model.reload();
|
||||
}
|
||||
};
|
||||
HelpLocation hl = new HelpLocation(HelpTopics.CODE_BROWSER, refreshAction.getName());
|
||||
refreshAction.setHelpLocation(hl);
|
||||
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, "A"));
|
||||
refreshAction.setDescription(
|
||||
"<html>Push at any time to refresh the current table of references.<br>" +
|
||||
"This button is highlighted when the data <i>may</i> be stale.<br>");
|
||||
|
||||
// Add a listener to
|
||||
DomainObjectListener listener = new DomainObjectListenerBuilder(model)
|
||||
.any(RESTORED, REFERENCE_ADDED, REFERENCE_REMOVED)
|
||||
.call(() -> {
|
||||
// signal that the data in the table does not match the program
|
||||
refreshAction.getToolBarData().setIcon(Icons.REFRESH_ICON);
|
||||
})
|
||||
.build();
|
||||
provider.setProgramListener(listener);
|
||||
provider.addLocalAction(refreshAction);
|
||||
}
|
||||
|
||||
// Note: this action lives in this class so that the action can hold a reference to a domain
|
||||
// object listener. The action will hold a reference so that as long as the the provider is
|
||||
// around, the action will also be around to hold a reference to the listener.
|
||||
private static class DeleteXrefsAction extends DockingAction {
|
||||
|
||||
private TableComponentProvider<ReferenceEndpoint> provider;
|
||||
private Program program;
|
||||
private ReferencesFromTableModel tableModel;
|
||||
|
||||
public DeleteXrefsAction(TableComponentProvider<ReferenceEndpoint> provider,
|
||||
ReferencesFromTableModel tableModel, Program program) {
|
||||
super("Delete Reference", provider.getActionOwner(), KeyBindingType.SHARED);
|
||||
this.provider = provider;
|
||||
this.tableModel = tableModel;
|
||||
this.program = program;
|
||||
|
||||
setToolBarData(new ToolBarData(Icons.DELETE_ICON, "A"));
|
||||
setHelpLocation(new HelpLocation(HelpTopics.CODE_BROWSER, getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext c) {
|
||||
Object object = c.getContextObject();
|
||||
if (!(object instanceof JTable table)) {
|
||||
return false;
|
||||
}
|
||||
if (tableModel.isBusy()) {
|
||||
return false;
|
||||
}
|
||||
return table.getSelectedRowCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext c) {
|
||||
Object object = c.getContextObject();
|
||||
JTable table = (JTable) object;
|
||||
int[] rows = table.getSelectedRows();
|
||||
PluginTool tool = provider.getTool();
|
||||
deleteXrefs(tool, program, tableModel, rows);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void deleteXrefs(PluginTool tool, Program program,
|
||||
ReferencesFromTableModel tableModel, int[] rows) {
|
||||
|
||||
if (promptToDeleteXrefsDialog == null) {
|
||||
promptToDeleteXrefsDialog =
|
||||
new OptionDialogBuilder("Delete Xrefs?",
|
||||
"Do you wish to permanently delete the selected xrefs?")
|
||||
.addOption("Delete")
|
||||
.addCancel()
|
||||
.addDontShowAgainOption()
|
||||
.build();
|
||||
}
|
||||
|
||||
int choice = promptToDeleteXrefsDialog.show();
|
||||
if (choice != OptionDialog.YES_OPTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ReferenceEndpoint> deletedRowObjects = new ArrayList<>();
|
||||
CompoundCmd<Program> compoundCmd = new CompoundCmd<>("Delete References");
|
||||
for (int row : rows) {
|
||||
ReferenceEndpoint endpoint = tableModel.getRowObject(row);
|
||||
deletedRowObjects.add(endpoint);
|
||||
Reference ref = endpoint.getReference();
|
||||
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
|
||||
compoundCmd.add(cmd);
|
||||
}
|
||||
tool.execute(compoundCmd, program);
|
||||
|
||||
// also remove the object from the table, since they have been deleted
|
||||
deletedRowObjects.forEach(ro -> tableModel.removeObject(ro));
|
||||
}
|
||||
|
||||
private static String generateXRefTitle(ProgramLocation location) {
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
|
@ -39,7 +39,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
|
|||
|
||||
@Override
|
||||
protected Address getFromReferenceAddress(ProgramLocation programLocation) {
|
||||
return ((VariableXRefFieldLocation) programLocation).getRefAddress();
|
||||
return programLocation.getRefAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +63,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
|
|||
VariableLocation variableLocation = (VariableLocation) location;
|
||||
Variable variable = variableLocation.getVariable();
|
||||
|
||||
Set<Reference> refs = getVariableRefs(variable);
|
||||
Supplier<Collection<Reference>> refs = () -> getVariableRefs(variable);
|
||||
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Set;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
|
@ -98,7 +99,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
}
|
||||
|
||||
protected Address getFromReferenceAddress(ProgramLocation programLocation) {
|
||||
return ((XRefFieldLocation) programLocation).getRefAddress();
|
||||
return programLocation.getRefAddress();
|
||||
}
|
||||
|
||||
protected void showXRefDialog(Navigatable navigatable, ProgramLocation location,
|
||||
|
@ -108,7 +109,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return;
|
||||
}
|
||||
|
||||
Set<Reference> refs = XReferenceUtils.getAllXrefs(location);
|
||||
Supplier<Collection<Reference>> refs = () -> XReferenceUtils.getAllXrefs(location);
|
||||
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import resources.Icons;
|
|||
*/
|
||||
public abstract class AbstractSelectionNavigationAction extends ToggleDockingAction {
|
||||
|
||||
private static final Icon ICON = Icons.NAVIGATE_ON_INCOMING_EVENT_ICON;
|
||||
private static final Icon ICON = Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON;
|
||||
private static final String SELECTED_STATE = "SELECTION_NAVIGATION_SELECTED_STATE";
|
||||
|
||||
private SelectionListener selectionListener;
|
||||
|
@ -60,7 +60,6 @@ public abstract class AbstractSelectionNavigationAction extends ToggleDockingAct
|
|||
setDescription(HTMLUtilities.toHTML("Toggle <b>on</b> means to navigate to the location\n" +
|
||||
"in the program that corresponds to the selected row,\n as the selection changes."));
|
||||
setHelpLocation(new HelpLocation("Search", "Selection_Navigation"));
|
||||
setEnabled(true);
|
||||
setSelected(true); // toggle button; enabled by default
|
||||
|
||||
initialize();
|
||||
|
|
|
@ -18,8 +18,7 @@ package ghidra.util.table;
|
|||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
|
@ -45,16 +44,21 @@ import ghidra.util.task.TaskMonitor;
|
|||
*/
|
||||
public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEndpoint> {
|
||||
|
||||
private List<IncomingReferenceEndpoint> refs;
|
||||
private Supplier<Collection<Reference>> refsSupplier;
|
||||
|
||||
public ReferencesFromTableModel(Collection<Reference> refs, ServiceProvider sp,
|
||||
Program program) {
|
||||
super("References", sp, program, null);
|
||||
|
||||
this.refs = refs.stream().map(r -> {
|
||||
boolean offcut = ReferenceUtils.isOffcut(program, r.getToAddress());
|
||||
return new IncomingReferenceEndpoint(r, offcut);
|
||||
}).collect(Collectors.toList());
|
||||
this.refsSupplier = () -> refs;
|
||||
|
||||
addTableColumn(new ReferenceTypeTableColumn());
|
||||
}
|
||||
|
||||
public ReferencesFromTableModel(Supplier<Collection<Reference>> refsSupplier,
|
||||
ServiceProvider sp, Program program) {
|
||||
super("References", sp, program, null);
|
||||
this.refsSupplier = refsSupplier;
|
||||
|
||||
addTableColumn(new ReferenceTypeTableColumn());
|
||||
}
|
||||
|
@ -62,7 +66,13 @@ public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEn
|
|||
@Override
|
||||
protected void doLoad(Accumulator<ReferenceEndpoint> accumulator, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
refs.forEach(r -> accumulator.add(r));
|
||||
|
||||
Collection<Reference> xrefs = refsSupplier.get();
|
||||
for (Reference xref : xrefs) {
|
||||
boolean offcut = ReferenceUtils.isOffcut(program, xref.getToAddress());
|
||||
IncomingReferenceEndpoint endpoint = new IncomingReferenceEndpoint(xref, offcut);
|
||||
accumulator.add(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -112,7 +112,7 @@ public class DeleteTableRowAction extends DockingAction {
|
|||
|
||||
protected void removeSelectedItems() {
|
||||
TableModel model = table.getModel();
|
||||
if (!(model instanceof RowObjectTableModel)) {
|
||||
if (!(model instanceof ThreadedTableModel)) {
|
||||
throw new AssertException("This action cannot delete rows for the given table model." +
|
||||
"You can override this method to peform the delete action yourself.");
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ public class DeleteTableRowAction extends DockingAction {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean checkForBusy(TableModel model) {
|
||||
protected boolean checkForBusy(TableModel model) {
|
||||
|
||||
if (!(model instanceof ThreadedTableModel)) {
|
||||
return false;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
|
|
@ -16,12 +16,17 @@
|
|||
package ghidra.app.plugin.core.navigation.locationreferences;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.ListSelectionModel;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.cmd.function.SetReturnDataTypeCmd;
|
||||
import ghidra.app.cmd.refs.RemoveReferenceCmd;
|
||||
import ghidra.app.plugin.core.clear.ClearCmd;
|
||||
|
@ -142,8 +147,8 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
|||
|
||||
// 0100415a - sscanf
|
||||
Address address = addr(0x0100415a);
|
||||
int parameterColumn = 7;
|
||||
goTo(address, "Function Signature", parameterColumn);
|
||||
int returnTypeColumn = 7;
|
||||
goTo(address, "Function Signature", returnTypeColumn);
|
||||
|
||||
search();
|
||||
|
||||
|
@ -172,6 +177,33 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
|||
verifyReferenceAddresses(address, referenceAddresses);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteReferencesFromTable() {
|
||||
|
||||
// 01002cf5 - ghidra
|
||||
Address address = addr(0x01002cf5);
|
||||
int functionNameColumn = 15;
|
||||
goTo(address, "Function Signature", functionNameColumn);
|
||||
|
||||
search();
|
||||
|
||||
List<Address> referenceAddresses = getResultAddresses();
|
||||
int referenceCount = referenceAddresses.size();
|
||||
|
||||
DockingActionIf deleteAction =
|
||||
getAction(tool, locationReferencesPlugin.getName(), "Delete Reference");
|
||||
LocationReferencesProvider provider = getResultsProvider();
|
||||
assertFalse(isEnabled(deleteAction, provider));
|
||||
|
||||
selectRows(0);
|
||||
assertTrue(isEnabled(deleteAction, provider));
|
||||
performAction(deleteAction, provider, true);
|
||||
|
||||
referenceAddresses = getResultAddresses();
|
||||
int updatedReferenceCount = referenceAddresses.size();
|
||||
assertEquals(referenceCount - 1, updatedReferenceCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelLocationDescriptor() throws Exception {
|
||||
|
||||
|
@ -369,6 +401,19 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
|
|||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void selectRows(int... rows) {
|
||||
|
||||
LocationReferencesProvider provider = getResultsProvider();
|
||||
runSwing(() -> {
|
||||
GTable gTable = provider.getTable();
|
||||
ListSelectionModel selectionModel = gTable.getSelectionModel();
|
||||
for (int row : rows) {
|
||||
selectionModel.addSelectionInterval(row, row);
|
||||
}
|
||||
});
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void createString_CallStructure(String addressString) throws Exception {
|
||||
// String
|
||||
// "call_structure_A: %s\n",00
|
||||
|
|
|
@ -122,8 +122,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
int row = findRow("ghidra");
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
doubleClick(symbolTable, row, SymbolTableModel.LOCATION_COL);
|
||||
ProgramLocation pl = getProgramLocation(row, SymbolTableModel.LOCATION_COL, model);
|
||||
doubleClick(symbolTable, row, AbstractSymbolTableModel.LOCATION_COL);
|
||||
ProgramLocation pl = getProgramLocation(row, AbstractSymbolTableModel.LOCATION_COL, model);
|
||||
assertEquals(pl.getAddress(), cbPlugin.getCurrentAddress());
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingLabelColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LABEL_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -141,7 +141,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
|
||||
}
|
||||
|
||||
sortDescending(SymbolTableModel.LABEL_COL);
|
||||
sortDescending(AbstractSymbolTableModel.LABEL_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -193,7 +193,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingAddressColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LOCATION_COL);
|
||||
|
||||
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -202,7 +202,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(loc1.compareTo(loc2) <= 0);
|
||||
}
|
||||
|
||||
sortDescending(SymbolTableModel.LOCATION_COL);
|
||||
sortDescending(AbstractSymbolTableModel.LOCATION_COL);
|
||||
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
AddressBasedLocation loc1 = getLocation(i);
|
||||
|
@ -215,7 +215,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testSortingReferenceColumn() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
sortAscending(AbstractSymbolTableModel.REFS_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -224,7 +224,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertTrue(refs1.compareTo(refs2) <= 0);
|
||||
}
|
||||
|
||||
sortDescending(SymbolTableModel.REFS_COL);
|
||||
sortDescending(AbstractSymbolTableModel.REFS_COL);
|
||||
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
Integer refs1 = getRefCount(i);
|
||||
|
@ -325,12 +325,10 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
public void testEditing() throws Exception {
|
||||
openProgram("sample");
|
||||
|
||||
waitForNotBusy();
|
||||
|
||||
String symbolName = "ghidra";
|
||||
int row = findRow(symbolName);
|
||||
|
||||
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL);
|
||||
doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
|
||||
waitForSwing();
|
||||
assertTrue(symbolTable.isEditing());
|
||||
|
||||
|
@ -361,7 +359,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
String symbolName = "ghidra";
|
||||
int row = findRow(symbolName);
|
||||
|
||||
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL);
|
||||
doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
|
||||
waitForSwing();
|
||||
assertTrue(symbolTable.isEditing());
|
||||
|
||||
|
@ -782,6 +780,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
Address from = sample.getNewAddress(0x01004aea);
|
||||
Address to = sample.getNewAddress(0x50);
|
||||
Reference ref = getReference(from, to);
|
||||
assertNotNull(ref);
|
||||
|
||||
tx(program, () -> {
|
||||
ReferenceManager manager = program.getReferenceManager();
|
||||
|
@ -847,7 +846,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** LABEL **********************/
|
||||
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LABEL_COL);
|
||||
|
||||
TableModel model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -860,7 +859,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** ADDRESS **********************/
|
||||
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LOCATION_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -874,7 +873,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
/************** REFERENCES **********************/
|
||||
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
sortAscending(AbstractSymbolTableModel.REFS_COL);
|
||||
|
||||
model = symbolTable.getModel();
|
||||
for (int i = 0; i < model.getRowCount() - 1; ++i) {
|
||||
|
@ -893,7 +892,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
showReferencesTable();
|
||||
|
||||
singleClick(symbolTable, findRow("ghidra", "Global"), SymbolTableModel.LABEL_COL);
|
||||
singleClick(symbolTable, findRow("ghidra", "Global"), AbstractSymbolTableModel.LABEL_COL);
|
||||
|
||||
/*****************************/
|
||||
|
||||
|
@ -1003,7 +1002,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
deleteText(textField);
|
||||
|
||||
// sort on a different column to trigger the other kind of filtering
|
||||
sortAscending(SymbolTableModel.REFS_COL);
|
||||
sortAscending(AbstractSymbolTableModel.REFS_COL);
|
||||
|
||||
text = "_";
|
||||
myTypeText(textField, text);
|
||||
|
@ -1074,7 +1073,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
deleteText(textField);
|
||||
|
||||
// sort on a different column to trigger the other kind of filtering
|
||||
sortAscending(SymbolTableModel.LOCATION_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LOCATION_COL);
|
||||
|
||||
text = "_";
|
||||
myTypeText(textField, text);
|
||||
|
@ -1140,7 +1139,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceRemvoed_ReferenceToDynamicSymbol() throws Exception {
|
||||
public void testReferenceRemoved_ReferenceToDynamicSymbol() throws Exception {
|
||||
|
||||
openProgram("sample");
|
||||
|
||||
|
@ -1153,10 +1152,63 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertFalse(row > -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteReference() throws Exception {
|
||||
|
||||
openProgram("sample");
|
||||
|
||||
showReferencesTable();
|
||||
ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class);
|
||||
|
||||
// pick symbol with refs
|
||||
selectRow("ghidra");
|
||||
|
||||
int refCount = referenceTable.getModel().getRowCount();
|
||||
assertReference("01004101", "00000052", true);
|
||||
|
||||
DockingActionIf deleteRefsAction =
|
||||
getAction(tool, plugin.getName(), "Delete Reference");
|
||||
assertFalse(isEnabled(deleteRefsAction, referencesProvider));
|
||||
|
||||
selectRef(referenceTable, "01004101", "00000052");
|
||||
assertTrue(isEnabled(deleteRefsAction, referencesProvider));
|
||||
|
||||
performAction(deleteRefsAction, referencesProvider, true);
|
||||
assertEquals(refCount - 1, referenceTable.getModel().getRowCount());
|
||||
assertReference("01004101", "00000052", false);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Helper methods
|
||||
//==================================================================================================
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void selectRef(GTable table, String from, String to) throws Exception {
|
||||
|
||||
Reference ref = getReference(addr(from), addr(to));
|
||||
int viewRow = runSwing(() -> {
|
||||
RowObjectTableModel<Reference> model =
|
||||
(RowObjectTableModel<Reference>) table.getModel();
|
||||
return model.getRowIndex(ref);
|
||||
});
|
||||
|
||||
selectRows(table, viewRow);
|
||||
}
|
||||
|
||||
private void assertReference(String fromString, String toString, boolean exists) {
|
||||
|
||||
Address from = addr(fromString);
|
||||
Address to = addr(toString);
|
||||
Reference ref = getReference(from, to);
|
||||
if (exists) {
|
||||
assertNotNull(ref);
|
||||
}
|
||||
else {
|
||||
assertNull(ref);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Reference getReference(Address from, Address to) {
|
||||
|
||||
ReferenceManager rm = program.getReferenceManager();
|
||||
|
@ -1167,7 +1219,6 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
fail("Did not find expected mem reference between " + from + " and " + to);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1234,6 +1285,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
return popup;
|
||||
}
|
||||
|
||||
private void selectRow(String symbolName) {
|
||||
int row = findRow(symbolName);
|
||||
selectRow(row);
|
||||
}
|
||||
|
||||
private void selectRow(int row) {
|
||||
selectRows(row, row);
|
||||
|
||||
|
@ -1243,18 +1299,22 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
}
|
||||
|
||||
private void selectRows(int... rows) {
|
||||
selectRows(symbolTable, rows);
|
||||
}
|
||||
|
||||
private void selectRows(JTable table, int... rows) {
|
||||
assertNotNull(rows);
|
||||
assertTrue("Must have at least one row to select", rows.length > 0);
|
||||
runSwing(() -> {
|
||||
|
||||
symbolTable.clearSelection();
|
||||
table.clearSelection();
|
||||
|
||||
for (int row : rows) {
|
||||
symbolTable.addRowSelectionInterval(row, row);
|
||||
table.addRowSelectionInterval(row, row);
|
||||
}
|
||||
int end = rows[rows.length - 1];
|
||||
Rectangle rect = symbolTable.getCellRect(end, 0, true);
|
||||
symbolTable.scrollRectToVisible(rect);
|
||||
Rectangle rect = table.getCellRect(end, 0, true);
|
||||
table.scrollRectToVisible(rect);
|
||||
});
|
||||
waitForSwing();
|
||||
}
|
||||
|
@ -1326,14 +1386,15 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
private Integer getRefCount(int row) {
|
||||
Integer count =
|
||||
runSwing(() -> (Integer) symbolModel.getValueAt(row, SymbolTableModel.REFS_COL));
|
||||
runSwing(
|
||||
() -> (Integer) symbolModel.getValueAt(row, AbstractSymbolTableModel.REFS_COL));
|
||||
return count;
|
||||
}
|
||||
|
||||
private AddressBasedLocation getLocation(int row) {
|
||||
AddressBasedLocation location =
|
||||
runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row,
|
||||
SymbolTableModel.LOCATION_COL));
|
||||
AbstractSymbolTableModel.LOCATION_COL));
|
||||
return location;
|
||||
}
|
||||
|
||||
|
@ -1650,16 +1711,17 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
|
||||
waitForNotBusy();
|
||||
|
||||
sortAscending(SymbolTableModel.LABEL_COL);
|
||||
sortAscending(AbstractSymbolTableModel.LABEL_COL);
|
||||
}
|
||||
|
||||
private void showReferencesTable() {
|
||||
private void showReferencesTable() throws Exception {
|
||||
|
||||
performAction(viewRefAction, true);
|
||||
ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class);
|
||||
referenceTable =
|
||||
(GTable) findComponentByName(referencesProvider.getComponent(), "Reference Table");
|
||||
assertNotNull(referenceTable);
|
||||
waitForNotBusy();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -17,6 +17,9 @@ package ghidra.app.util.viewer.field;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
|
||||
import org.junit.*;
|
||||
|
@ -43,6 +46,7 @@ import ghidra.program.util.XRefHeaderFieldLocation;
|
|||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.table.GhidraProgramTableModel;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
|
||||
/**
|
||||
* Tests that references are displayed correctly when selecting the XRef field in
|
||||
|
@ -162,6 +166,27 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
assertEquals(3, model.getRowCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteReferencesFromTable() {
|
||||
|
||||
doubleClickXRef("1001005", "XREF[1]: ");
|
||||
ComponentProvider comp = waitForComponentProvider(TableComponentProvider.class);
|
||||
TableComponentProvider<?> table = (TableComponentProvider<?>) comp;
|
||||
assertEquals(1, table.getModel().getRowCount());
|
||||
assertReference("01001009", "01001005", true);
|
||||
|
||||
DockingActionIf deleteAction = getAction(tool, "TableServicePlugin", "Delete Reference");
|
||||
assertFalse(runSwing(() -> deleteAction.isEnabled()));
|
||||
|
||||
selectRow(table, 0);
|
||||
assertTrue(runSwing(() -> deleteAction.isEnabled()));
|
||||
|
||||
performAction(deleteAction, table, true);
|
||||
|
||||
assertEquals(0, table.getModel().getRowCount());
|
||||
assertReference("01001009", "01001005", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testViewReferencesShowThunkXrefs_FromThunk() {
|
||||
|
||||
|
@ -218,6 +243,42 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
|
|||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void assertReference(String fromString, String toString, boolean exists) {
|
||||
|
||||
Address from = addr(fromString);
|
||||
Address to = addr(toString);
|
||||
List<Reference> refs = getReferences(to);
|
||||
if (exists) {
|
||||
assertEquals(1, refs.size());
|
||||
Reference ref = refs.get(0);
|
||||
Address refFrom = ref.getFromAddress();
|
||||
Address refTo = ref.getToAddress();
|
||||
assertEquals(from, refFrom);
|
||||
assertEquals(to, refTo);
|
||||
}
|
||||
else {
|
||||
assertEquals(0, refs.size());
|
||||
}
|
||||
}
|
||||
|
||||
private List<Reference> getReferences(Address to) {
|
||||
List<Reference> refs = new ArrayList<>();
|
||||
ReferenceManager rm = program.getReferenceManager();
|
||||
ReferenceIterator it = rm.getReferencesTo(to);
|
||||
while (it.hasNext()) {
|
||||
refs.add(it.next());
|
||||
}
|
||||
return refs;
|
||||
}
|
||||
|
||||
private void selectRow(TableComponentProvider<?> provider, int row) {
|
||||
runSwing(() -> {
|
||||
GhidraTable gTable = provider.getTable();
|
||||
gTable.selectRow(row);
|
||||
});
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void createThunkFunction(String thunkAddressString, String thunkedAddressString) {
|
||||
|
||||
Address thunkAddress = addr(thunkAddressString);
|
||||
|
|
|
@ -86,7 +86,10 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
|
|||
private GTree inTree;
|
||||
private GTree outTree;
|
||||
private boolean isPrimary;
|
||||
private enum Condition {IN, OUT, EITHER}
|
||||
|
||||
private enum Condition {
|
||||
IN, OUT, EITHER
|
||||
}
|
||||
|
||||
private SwingUpdateManager reloadUpdateManager = new SwingUpdateManager(500, () -> doUpdate());
|
||||
|
||||
|
@ -286,12 +289,13 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
|
|||
//
|
||||
// navigate incoming nodes on selection
|
||||
//
|
||||
navigationIncomingAction = new ToggleActionBuilder("Navigate Incoming Location Changes", ownerName)
|
||||
navigationIncomingAction = new ToggleActionBuilder("Navigate on Incoming Location Changes", ownerName)
|
||||
.description(HTMLUtilities.toHTML("Incoming Navigation" +
|
||||
"<br><br>Toggle <b>On</b> - change the displayed " +
|
||||
"function on Listing navigation events" +
|
||||
"<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events"))
|
||||
.helpLocation(new HelpLocation(ownerName, "Call_Tree_Action_Incoming_Navigation"))
|
||||
.sharedKeyBinding()
|
||||
.toolBarGroup(navigationOptionsToolbarGroup, "2")
|
||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||
.selected(isPrimary)
|
||||
|
@ -305,7 +309,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
|
|||
//@formatter:on
|
||||
}
|
||||
|
||||
|
||||
private boolean isValidSelection(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
|
@ -390,7 +393,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
|
|||
return isValidContextMult(context, Condition.OUT);
|
||||
}
|
||||
|
||||
|
||||
private void expandToDepth(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
GTree gTree = (GTree) contextObject;
|
||||
|
|
|
@ -112,9 +112,12 @@ public class ComponentInfoDialog extends DialogComponentProvider implements Prop
|
|||
.build();
|
||||
addAction(eventAction);
|
||||
|
||||
toggleFollowFocusAction = new ToggleActionBuilder("Follow Focus", ACTION_OWNER)
|
||||
toggleFollowFocusAction =
|
||||
new ToggleActionBuilder("Navigate on Incoming Location Changes", ACTION_OWNER)
|
||||
.sharedKeyBinding()
|
||||
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
|
||||
.description("On causes component table to constant repopulate as focus changes")
|
||||
.description(
|
||||
"On causes component table to constant repopulate as focus changes")
|
||||
.onAction(c -> toggleFollowFocus())
|
||||
.selected(true)
|
||||
.build();
|
||||
|
|
|
@ -36,17 +36,17 @@ public interface Reference extends Comparable<Reference> {
|
|||
public static final int OTHER = -2;
|
||||
|
||||
/**
|
||||
* Get the address of the codeunit that is making the reference.
|
||||
* {@return Get the address of the code unit that is making the reference.}
|
||||
*/
|
||||
public Address getFromAddress();
|
||||
|
||||
/**
|
||||
* Get the "to" address for this reference.
|
||||
* {@return Get the "to" address for this reference.}
|
||||
*/
|
||||
public Address getToAddress();
|
||||
|
||||
/**
|
||||
* Return whether this reference is marked as primary.
|
||||
* {@return Return whether this reference is marked as primary.}
|
||||
*/
|
||||
public boolean isPrimary();
|
||||
|
||||
|
@ -59,7 +59,7 @@ public interface Reference extends Comparable<Reference> {
|
|||
public long getSymbolID();
|
||||
|
||||
/**
|
||||
* Get the type of reference being made.
|
||||
* {@return Get the type of reference being made.}
|
||||
*/
|
||||
public RefType getReferenceType();
|
||||
|
||||
|
@ -71,50 +71,50 @@ public interface Reference extends Comparable<Reference> {
|
|||
public int getOperandIndex();
|
||||
|
||||
/**
|
||||
* Return true if this reference is on the Mnemonic and not on an operand
|
||||
* {@return Return true if this reference is on the Mnemonic and not on an operand.}
|
||||
*/
|
||||
public boolean isMnemonicReference();
|
||||
|
||||
/**
|
||||
* Return true if this reference is on an operand and not on the Mnemonic.
|
||||
* {@return Return true if this reference is on an operand and not on the Mnemonic.}
|
||||
*/
|
||||
public boolean isOperandReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference is an instance of StackReference and
|
||||
* refers to a stack location.
|
||||
* {@return Returns true if this reference is an instance of StackReference and
|
||||
* refers to a stack location.}
|
||||
*/
|
||||
public boolean isStackReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference is an instance of ExternalReference.
|
||||
* {@return Returns true if this reference is an instance of ExternalReference.}
|
||||
*/
|
||||
public boolean isExternalReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference is an instance of EntryReference.
|
||||
* {@return Returns true if this reference is an instance of EntryReference.}
|
||||
*/
|
||||
public boolean isEntryPointReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference to an address in the programs memory
|
||||
* space. This includes offset and shifted references.
|
||||
* {@return Returns true if this reference to an address in the programs memory
|
||||
* space. This includes offset and shifted references.}
|
||||
*/
|
||||
public boolean isMemoryReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference to an address in the programs register
|
||||
* space.
|
||||
* {@return Returns true if this reference to an address in the programs register
|
||||
* space.}
|
||||
*/
|
||||
public boolean isRegisterReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference is an instance of OffsetReference.
|
||||
* {@return Returns true if this reference is an instance of OffsetReference.}
|
||||
*/
|
||||
public boolean isOffsetReference();
|
||||
|
||||
/**
|
||||
* Returns true if this reference is an instance of ShiftedReference.
|
||||
* {@return Returns true if this reference is an instance of ShiftedReference.}
|
||||
*/
|
||||
public boolean isShiftedReference();
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public interface ReferenceManager {
|
|||
* an operand will be made primary by default. All non-memory references
|
||||
* will be removed from the specified operand. Certain reference {@link RefType types}
|
||||
* may not be specified (e.g., {@link RefType#FALL_THROUGH}).
|
||||
* @param fromAddr address of the codeunit where the reference occurs
|
||||
* @param fromAddr address of the code unit where the reference occurs
|
||||
* @param toAddr address of the location being referenced.
|
||||
* Memory, stack, and register addresses are all permitted.
|
||||
* @param type reference type - how the location is being referenced.
|
||||
|
@ -189,7 +189,7 @@ public interface ReferenceManager {
|
|||
* @param source the source of this reference
|
||||
* @param type reference type - how the location is being referenced
|
||||
* @return external reference
|
||||
* @throws InvalidInputException
|
||||
* @throws InvalidInputException if the input is invalid
|
||||
*/
|
||||
public Reference addExternalReference(Address fromAddr, int opIndex, ExternalLocation location,
|
||||
SourceType source, RefType type) throws InvalidInputException;
|
||||
|
@ -204,7 +204,7 @@ public interface ReferenceManager {
|
|||
/**
|
||||
* Remove all stack, external, and memory references for the given
|
||||
* from address.
|
||||
* @param fromAddr the address of the codeunit from which to remove all references.
|
||||
* @param fromAddr the address of the code unit from which to remove all references.
|
||||
*/
|
||||
public void removeAllReferencesFrom(Address fromAddr);
|
||||
|
||||
|
@ -247,7 +247,7 @@ public interface ReferenceManager {
|
|||
|
||||
/**
|
||||
* Get all flow references from the given address.
|
||||
* @param addr the address of the codeunit to get all flows from.
|
||||
* @param addr the address of the code unit to get all flows from.
|
||||
* @return get all flow references from the given address.
|
||||
*
|
||||
*/
|
||||
|
@ -279,7 +279,7 @@ public interface ReferenceManager {
|
|||
/**
|
||||
* Get the reference that has the given from and to address, and
|
||||
* operand index.
|
||||
* @param fromAddr the address of the codeunit making the reference.
|
||||
* @param fromAddr the address of the code unit making the reference.
|
||||
* @param toAddr the address being referred to.
|
||||
* @param opIndex the operand index.
|
||||
* @return reference which satisfies the specified criteria or null
|
||||
|
@ -306,7 +306,7 @@ public interface ReferenceManager {
|
|||
* address/opIndex. Keep in mind this is a rather inefficient
|
||||
* method as it must examine all references from the specified
|
||||
* fromAddr.
|
||||
* @param fromAddr the address of the codeunit being tested
|
||||
* @param fromAddr the address of the code unit being tested
|
||||
* @param opIndex the index of the operand being tested.
|
||||
* @return true if one or more reference from the specified address
|
||||
* and opindex are defined, else false
|
||||
|
@ -316,7 +316,7 @@ public interface ReferenceManager {
|
|||
/**
|
||||
* Returns true if there are any memory references at the given
|
||||
* address.
|
||||
* @param fromAddr the address of the codeunit being tested
|
||||
* @param fromAddr the address of the code unit being tested
|
||||
* @return true if one or more reference from the specified address
|
||||
* are defined, else false
|
||||
*/
|
||||
|
@ -378,18 +378,20 @@ public interface ReferenceManager {
|
|||
|
||||
/**
|
||||
* Returns the number of references from the specified <code>fromAddr</code>.
|
||||
* @param fromAddr the address of the codeunit making the reference.
|
||||
* @param fromAddr the address of the code unit making the reference.
|
||||
* @return the number of references from the specified <code>fromAddr</code>.
|
||||
*/
|
||||
public int getReferenceCountFrom(Address fromAddr);
|
||||
|
||||
/**
|
||||
* Return the number of references for "to" addresses.
|
||||
* @return the number of references for "to" addresses.
|
||||
*/
|
||||
public int getReferenceDestinationCount();
|
||||
|
||||
/**
|
||||
* Return the number of references for "from" addresses.
|
||||
* @return the number of references for "from" addresses.
|
||||
*/
|
||||
public int getReferenceSourceCount();
|
||||
|
||||
|
@ -401,7 +403,7 @@ public interface ReferenceManager {
|
|||
public boolean hasReferencesTo(Address toAddr);
|
||||
|
||||
/**
|
||||
* Uodate the reference type on a memory reference.
|
||||
* Update the reference type on a memory reference.
|
||||
* @param ref reference to be updated
|
||||
* @param refType new reference type
|
||||
* @return updated reference
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue