GP-2140 - Xrefs - Added an action to delete xrefs from the xrefs table

This commit is contained in:
dragonmacher 2025-06-16 13:46:34 -04:00
parent e804e1a01d
commit 1fc7356080
35 changed files with 1032 additions and 278 deletions

View file

@ -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>

View file

@ -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

Before After
Before After

View file

@ -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>

View file

@ -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 &lt;Ctrl&gt; 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 &lt;Ctrl&gt; 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>

View file

@ -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

View file

@ -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"))

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -1395,7 +1395,7 @@ public final class ReferenceUtils {
return;
}
Address[] thunkAddrs = func.getFunctionThunkAddresses();
Address[] thunkAddrs = func.getFunctionThunkAddresses(false);
if (thunkAddrs == null) {
return;
}

View file

@ -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() + ")";
}

View file

@ -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;
}
}

View file

@ -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;
}
//==================================================================================================

View file

@ -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());
}

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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")

View file

@ -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);

View file

@ -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;

View file

@ -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();

View file

@ -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();

View file

@ -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