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

@ -132,7 +132,20 @@
<P align="left">This dialog lists all the Xref addresses, any labels that are at that address <P align="left">This dialog lists all the Xref addresses, any labels that are at that address
and a preview of the instruction at that address. Clicking on any row in the table will cause 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> 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 <H3><A NAME="Show_Thunk_Xrefs"></A>Show Thunk Xrefs
<IMG border="0" src="images/ThunkFunction.gif" alt=""></H3> <IMG border="0" src="images/ThunkFunction.gif" alt=""></H3>
@ -147,6 +160,17 @@
point to the function used to launch the dialog. point to the function used to launch the dialog.
</P> </P>
</BLOCKQUOTE> </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> </BLOCKQUOTE>

View file

@ -126,22 +126,36 @@
table. You may also access this feature by right-clicking an item in the table and table. You may also access this feature by right-clicking an item in the table and
selecting <B>Make Selection</B>.</P> selecting <B>Make Selection</B>.</P>
<BLOCKQUOTE>
<P>
<IMG alt="" src="help/shared/tip.png">
To instead make a Program Selection Highlight, use the select button mentioned above.
Then, click from the menu bar <B>Select
<img src="help/shared/arrow.gif" alt="-&gt;" border="0">
Program Highlight
<img src="help/shared/arrow.gif" alt="-&gt;" border="0">
Entire Selection
</B>
</P>
</BLOCKQUOTE>
<P align="left"><A name="Highlight"></A>The <IMG alt="" src="images/tag_yellow.png"> <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 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 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 a <A HREF="help/topics/SetHighlightPlugin/Highlighting.htm">Program Selection
Highlight</A>. Highlight</A>.
</P> </P>
<BLOCKQUOTE>
<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> <P>
<IMG alt="" src="help/shared/tip.png"> <IMG alt="" src="help/shared/note.yellow.png">Not all table rows can be deleted. This is
To instead make a Program Selection Highlight, use the select button mentioned above. because the table shows not just items that have a database reference, but also items
Then, click from the menu bar <B>Select that are dynamic references, as well as general uses of an item, such as a data access
<img src="help/shared/arrow.gif" alt="-&gt;" border="0"> generated by the Decompiler. The delete action will only allow you to delete references
Program Highlight that are in the database.
<img src="help/shared/arrow.gif" alt="-&gt;" border="0">
Entire Selection
</B>
</P> </P>
</BLOCKQUOTE> </BLOCKQUOTE>

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>
</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>
</P>
<BLOCKQUOTE>
<P>This action will delete all selected references from the database.
</P>
</BLOCKQUOTE>
<P>
<BR>
<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> </BODY>
</HTML> </HTML>

View file

@ -164,6 +164,41 @@
</BLOCKQUOTE> </BLOCKQUOTE>
</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> <H2>Renaming a Symbol</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
@ -198,29 +233,6 @@
<A href="help/topics/SymbolTreePlugin/SymbolTree.htm#Edit_External_Location">Edit External Location</A> <A href="help/topics/SymbolTreePlugin/SymbolTree.htm#Edit_External_Location">Edit External Location</A>
for more discussion on the use of the edit dialog). for more discussion on the use of the edit dialog).
</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="Pinning a Symbol"></A>Pinning a Symbol</H2> <H2><A name="Pinning a Symbol"></A>Pinning a Symbol</H2>
@ -250,18 +262,16 @@
</LI> </LI>
</OL> </OL>
</BLOCKQUOTE> </BLOCKQUOTE>
<H2><A name="Set_Filter"></A>Filtering <IMG src="images/view-filter.png"></H2> <H2><A name="Set_Filter"></A>Filtering</H2>
<BLOCKQUOTE> <BLOCKQUOTE>
<P>The list of displayed symbols is determined by the current symbol table settings. These <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.. 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 The displayed symbols will correspond to the selected checkboxes in the <I>Symbol Table
Filter</I> dialog.</P> Filter</I> dialog.</P>
</BLOCKQUOTE> </BLOCKQUOTE>
<P align="center"><IMG src="images/view-filter.png"></P>
<P align="center"><I>Symbol Table Filter</I> Dialog</P> <P align="center"><I>Symbol Table Filter</I> Dialog</P>
<P align="center"><IMG src="images/Filter.png"></P> <P align="center"><IMG src="images/Filter.png"></P>
@ -606,7 +616,6 @@
</BLOCKQUOTE> </BLOCKQUOTE>
<P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P> <P class="providedbyplugin">Provided by: <I>Symbol Table Plugin</I></P>
<P class="relatedtopic">Related Topics</P> <P class="relatedtopic">Related Topics</P>
<UL> <UL>

View file

@ -454,7 +454,9 @@ public class CallTreeProvider extends ComponentProviderAdapter {
// navigate incoming nodes on selection // navigate incoming nodes on selection
// //
navigateIncomingAction = navigateIncomingAction =
new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName()) { new ToggleDockingAction("Navigation Incoming Location Changes", plugin.getName(),
KeyBindingType.SHARED) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
// handled later as we receive events // handled later as we receive events

View file

@ -336,14 +336,16 @@ class MemoryMapProvider extends ComponentProviderAdapter {
action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A' action.getToolBarData().setToolBarGroup("B"); // the other actions are in group 'A'
tool.addLocalAction(this, action); tool.addLocalAction(this, action);
toggleNavigateAction = new ToggleActionBuilder("Memory Map Navigation", plugin.getName()) toggleNavigateAction =
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) new ToggleActionBuilder("Navigate on Incoming Location Changes", plugin.getName())
.selected(false) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.helpLocation(new HelpLocation("MemoryMapPlugin", "Navigation")) .selected(false)
.description(HTMLUtilities.toHTML("Toggle <b>on</b> means to select the block" + .sharedKeyBinding()
" that contains the current location")) .helpLocation(new HelpLocation("MemoryMapPlugin", "Navigation"))
.onAction(c -> followLocationChanges = toggleNavigateAction.isSelected()) .description(HTMLUtilities.toHTML("Toggle <b>on</b> means to select the block" +
.buildAndInstallLocal(this); " that contains the current location"))
.onAction(c -> followLocationChanges = toggleNavigateAction.isSelected())
.buildAndInstallLocal(this);
} }
private boolean checkExclusiveAccess() { private boolean checkExclusiveAccess() {

View file

@ -20,6 +20,7 @@ import static ghidra.app.plugin.core.navigation.locationreferences.LocationRefer
import java.util.Objects; import java.util.Objects;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.DynamicReference;
import ghidra.program.model.symbol.Reference; import ghidra.program.model.symbol.Reference;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
@ -36,6 +37,11 @@ public class LocationReference implements Comparable<LocationReference> {
private final LocationReferenceContext context; private final LocationReferenceContext context;
private final ProgramLocation location; private final ProgramLocation location;
/**
* Optional reference object. Some clients do not have actual references.
*/
private Reference reference;
private int hashCode = -1; private int hashCode = -1;
private static String getRefType(Reference r) { private static String getRefType(Reference r) {
@ -57,6 +63,7 @@ public class LocationReference implements Comparable<LocationReference> {
LocationReference(Reference reference, boolean isOffcutReference) { LocationReference(Reference reference, boolean isOffcutReference) {
this(reference.getFromAddress(), null, getRefType(reference), EMPTY_CONTEXT, this(reference.getFromAddress(), null, getRefType(reference), EMPTY_CONTEXT,
isOffcutReference); isOffcutReference);
this.reference = reference;
} }
LocationReference(Address locationOfUseAddress, String refType, boolean isOffcutReference) { LocationReference(Address locationOfUseAddress, String refType, boolean isOffcutReference) {
@ -133,6 +140,28 @@ public class LocationReference implements Comparable<LocationReference> {
return location; 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 @Override
public int hashCode() { public int hashCode() {
if (hashCode != -1) { if (hashCode != -1) {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,7 +18,7 @@ package ghidra.app.plugin.core.navigation.locationreferences;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Collection; import java.util.*;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
@ -88,6 +88,26 @@ public class LocationReferencesPanel extends JPanel {
tableModel.fullReload(); 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) { void addTableModelListener(TableModelListener listener) {
tableModel.addTableModelListener(listener); tableModel.addTableModelListener(listener);
} }

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import java.util.*; import java.util.*;
import java.util.function.Supplier;
import docking.ActionContext; import docking.ActionContext;
import docking.action.DockingAction; import docking.action.DockingAction;
@ -133,7 +134,7 @@ public class LocationReferencesPlugin extends Plugin
return; // not sure if this can happen 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); 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.BorderLayout;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import docking.ActionContext; import docking.ActionContext;
import docking.DefaultActionContext;
import docking.action.*; import docking.action.*;
import docking.action.builder.ActionBuilder;
import docking.widgets.table.GTable; import docking.widgets.table.GTable;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.nav.NavigatableRemovalListener; import ghidra.app.nav.NavigatableRemovalListener;
import ghidra.app.services.GoToService; import ghidra.app.services.GoToService;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.model.DomainObjectChangedEvent; import ghidra.framework.model.DomainObjectChangedEvent;
import ghidra.framework.model.DomainObjectListener; import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.plugintool.ComponentProviderAdapter; 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.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Reference;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@ -97,8 +101,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
program.addListener(this); program.addListener(this);
setTitle(TITLE_PREFIX_REFERENCES); setTitle(TITLE_PREFIX_REFERENCES);
setHelpLocation( setHelpLocation(new HelpLocation(getOwner(), "LocationReferencesPlugin"));
new HelpLocation(locationReferencesPlugin.getName(), "LocationReferencesPlugin"));
setWindowMenuGroup("References"); setWindowMenuGroup("References");
setTransient(); setTransient();
createView(); createView();
@ -235,7 +238,8 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
private void createActions() { private void createActions() {
homeAction = new DockingAction("Home", locationReferencesPlugin.getName()) { String toolbarGroup = "A";
homeAction = new DockingAction("Home", getOwner()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
goTo(currentLocationDescriptor.getHomeLocation(), 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(); updateHomeActionState();
selectionAction = 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.setSelected(true);
highlightAction.setDescription("Highlight matches in tool"); highlightAction.setDescription("Highlight matches in tool");
refreshAction = new DockingAction("Refresh", locationReferencesPlugin.getName()) { refreshAction = new DockingAction("Refresh", getOwner()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
updateManager.updateNow(); updateManager.updateNow();
} }
}; };
refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON)); refreshAction.setToolBarData(new ToolBarData(REFRESH_NOT_NEEDED_ICON, toolbarGroup));
refreshAction.setDescription( refreshAction.setDescription(
"<html>Push at any time to refresh the current table of references.<br>" + "<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>"); "This button is highlighted when the data <i>may</i> be stale.<br>");
@ -274,6 +278,31 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
SelectionNavigationAction selectionNavigationAction = SelectionNavigationAction selectionNavigationAction =
new SelectionNavigationAction(locationReferencesPlugin, referencesPanel.getTable()); 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(); GhidraTable table = referencesPanel.getTable();
DockingAction removeItemsAction = new DeleteAction(tool, table); DockingAction removeItemsAction = new DeleteAction(tool, table);
removeItemsAction.setEnabled(false); // off by default; updated when the user clicks the 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, refreshAction);
tool.addLocalAction(this, selectionAction); tool.addLocalAction(this, selectionAction);
tool.addLocalAction(this, highlightAction); tool.addLocalAction(this, highlightAction);
tool.addLocalAction(this, deleteRefAction);
tool.addLocalAction(this, removeItemsAction); tool.addLocalAction(this, removeItemsAction);
tool.addLocalAction(this, selectionNavigationAction); tool.addLocalAction(this, selectionNavigationAction);
setHelpLocation(); 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() { private void updateHomeActionState() {
// we have no home location sometimes, like when launched from the service interface when // we have no home location sometimes, like when launched from the service interface when
// looking for references to datatypes // looking for references to datatypes
@ -300,13 +344,10 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
} }
private void setHelpLocation() { private void setHelpLocation() {
homeAction.setHelpLocation(new HelpLocation(locationReferencesPlugin.getName(), "Home")); homeAction.setHelpLocation(new HelpLocation(getOwner(), "Home"));
refreshAction.setHelpLocation( refreshAction.setHelpLocation(new HelpLocation(getOwner(), "Refresh"));
new HelpLocation(locationReferencesPlugin.getName(), "Refresh")); selectionAction.setHelpLocation(new HelpLocation(getOwner(), "Select"));
selectionAction.setHelpLocation( highlightAction.setHelpLocation(new HelpLocation(getOwner(), "Highlight"));
new HelpLocation(locationReferencesPlugin.getName(), "Select"));
highlightAction.setHelpLocation(
new HelpLocation(locationReferencesPlugin.getName(), "Highlight"));
} }
private void goTo(ProgramLocation loc, Program theProgram) { private void goTo(ProgramLocation loc, Program theProgram) {
@ -318,7 +359,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
final JTable table = referencesPanel.getTable(); final JTable table = referencesPanel.getTable();
table.getSelectionModel().addListSelectionListener(e -> { table.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
selectionAction.setEnabled(table.getSelectedRowCount() > 0); contextChanged();
} }
}); });
@ -361,6 +402,15 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
Program getProgram() { Program getProgram() {
return program; return program;
} }
GTable getTable() {
return referencesPanel.getTable();
}
LocationReferencesPanel getPanel() {
return referencesPanel;
}
//================================================================================================== //==================================================================================================
// Interface methods // Interface methods
//================================================================================================== //==================================================================================================
@ -403,7 +453,7 @@ public class LocationReferencesProvider extends ComponentProviderAdapter
@Override @Override
public ActionContext getActionContext(MouseEvent event) { 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 { private class DeleteAction extends DeleteTableRowAction {
DeleteAction(PluginTool tool, GTable table) { DeleteAction(PluginTool tool, GTable table) {
super(table, locationReferencesPlugin.getName()); super(table, LocationReferencesProvider.this.getOwner());
} }
@Override @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; return;
} }
Address[] thunkAddrs = func.getFunctionThunkAddresses(); Address[] thunkAddrs = func.getFunctionThunkAddresses(false);
if (thunkAddrs == null) { if (thunkAddrs == null) {
return; return;
} }

View file

@ -16,6 +16,8 @@
package ghidra.app.plugin.core.symtable; package ghidra.app.plugin.core.symtable;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -23,10 +25,13 @@ import javax.swing.JComponent;
import docking.ActionContext; import docking.ActionContext;
import docking.WindowPosition; import docking.WindowPosition;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
import ghidra.app.util.SymbolInspector; import ghidra.app.util.SymbolInspector;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Swing; import ghidra.util.Swing;
@ -72,7 +77,21 @@ class ReferenceProvider extends ComponentProviderAdapter {
if (program == null) { if (program == null) {
return 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) { void setCurrentSymbol(Symbol symbol) {
@ -104,6 +123,10 @@ class ReferenceProvider extends ComponentProviderAdapter {
} }
} }
Program getProgram() {
return referenceKeyModel.getProgram();
}
void reload() { void reload() {
if (isVisible()) { if (isVisible()) {
referenceKeyModel.reload(); referenceKeyModel.reload();
@ -126,6 +149,17 @@ class ReferenceProvider extends ComponentProviderAdapter {
return referencePanel.getTable(); 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() { private String generateSubTitle() {
return "(" + referenceKeyModel.getDescription() + ")"; 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

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -32,7 +32,11 @@ import docking.widgets.table.RowFilterTransformer;
import ghidra.app.plugin.core.symtable.AbstractSymbolTableModel.OriginalNameColumn; import ghidra.app.plugin.core.symtable.AbstractSymbolTableModel.OriginalNameColumn;
import ghidra.framework.options.SaveState; import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginTool; 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.Symbol;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.util.ProgramLocation;
import ghidra.util.table.*; import ghidra.util.table.*;
class SymbolPanel extends JPanel { class SymbolPanel extends JPanel {
@ -41,9 +45,9 @@ class SymbolPanel extends JPanel {
private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS"; private static final String FILTER_SETTINGS_ELEMENT_NAME = "FILTER_SETTINGS";
private SymbolProvider symProvider; private SymbolProvider symbolProvider;
private SymbolTableModel tableModel; private SymbolTableModel symbolModel;
private GhidraTable symTable; private GhidraTable gTable;
private TableModelListener listener; private TableModelListener listener;
private FilterDialog filterDialog; private FilterDialog filterDialog;
private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel; private GhidraThreadedTablePanel<SymbolRowObject> threadedTablePanel;
@ -51,34 +55,31 @@ class SymbolPanel extends JPanel {
SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer, SymbolPanel(SymbolProvider provider, SymbolTableModel model, SymbolRenderer renderer,
PluginTool tool) { PluginTool tool) {
super(new BorderLayout()); super(new BorderLayout());
this.symProvider = provider; this.symbolProvider = provider;
this.tableModel = model; this.symbolModel = model;
this.threadedTablePanel = new GhidraThreadedTablePanel<>(model);
this.listener = e -> symbolProvider.updateTitle();
threadedTablePanel = new GhidraThreadedTablePanel<>(model); gTable = threadedTablePanel.getTable();
gTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL);
this.listener = e -> symProvider.updateTitle(); gTable.setRowSelectionAllowed(true);
gTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
symTable = threadedTablePanel.getTable(); gTable.getModel().addTableModelListener(listener);
symTable.setAutoLookupColumn(AbstractSymbolTableModel.LABEL_COL); gTable.getSelectionModel().addListSelectionListener(e -> {
symTable.setRowSelectionAllowed(true);
symTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
symTable.getModel().addTableModelListener(listener);
symTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) { if (!e.getValueIsAdjusting()) {
handleTableSelection(); 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++) { for (int i = 0; i < gTable.getColumnCount(); i++) {
TableColumn column = symTable.getColumnModel().getColumn(i); TableColumn column = gTable.getColumnModel().getColumn(i);
column.setCellRenderer(renderer); column.setCellRenderer(renderer);
if (column.getModelIndex() == AbstractSymbolTableModel.LABEL_COL) { if (column.getModelIndex() == AbstractSymbolTableModel.LABEL_COL) {
column.setCellEditor(new SymbolEditor()); column.setCellEditor(new SymbolEditor());
@ -91,11 +92,11 @@ class SymbolPanel extends JPanel {
filterDialog = new FilterDialog(tool); filterDialog = new FilterDialog(tool);
// enable dragging symbols out of the symbol table // enable dragging symbols out of the symbol table
new SymbolTableDragProvider(symTable, model); new SymbolTableDragProvider(gTable, model);
} }
private JPanel createFilterFieldPanel() { private JPanel createFilterFieldPanel() {
tableFilterPanel = new GhidraTableFilterPanel<>(symTable, tableModel); tableFilterPanel = new GhidraTableFilterPanel<>(gTable, symbolModel);
tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " + tableFilterPanel.setToolTipText("Filters the contents of the table on symbol " +
"names that start with the given pattern"); "names that start with the given pattern");
@ -120,17 +121,35 @@ class SymbolPanel extends JPanel {
return tableFilterPanel; return tableFilterPanel;
} }
protected RowFilterTransformer<SymbolRowObject> updateRowDataTransformer(boolean nameOnly) { void locationChanged(ProgramLocation location) {
TableColumnModel columnModel = symTable.getColumnModel();
if (nameOnly) { Program program = location.getProgram();
return new NameOnlyRowTransformer(tableModel, columnModel); 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() { void dispose() {
symTable.getModel().removeTableModelListener(listener); gTable.getModel().removeTableModelListener(listener);
symTable.dispose(); gTable.dispose();
threadedTablePanel.dispose(); threadedTablePanel.dispose();
tableFilterPanel.dispose(); tableFilterPanel.dispose();
filterDialog.dispose(); filterDialog.dispose();
@ -140,24 +159,24 @@ class SymbolPanel extends JPanel {
if (filterDialog == null) { if (filterDialog == null) {
return; return;
} }
if (symTable.isEditing()) { if (gTable.isEditing()) {
symTable.editingCanceled(null); gTable.editingCanceled(null);
} }
symProvider.setCurrentSymbol(null); symbolProvider.setCurrentSymbol(null);
symTable.clearSelection(); gTable.clearSelection();
filterDialog.adjustFilter(symProvider, tableModel); filterDialog.adjustFilter(symbolProvider, symbolModel);
} }
SymbolFilter getFilter() { SymbolFilter getFilter() {
return tableModel.getFilter(); return symbolModel.getFilter();
} }
void readConfigState(SaveState saveState) { void readConfigState(SaveState saveState) {
Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME); Element filterElement = saveState.getXmlElement(FILTER_SETTINGS_ELEMENT_NAME);
if (filterElement != null) { if (filterElement != null) {
filterDialog.restoreFilter(filterElement); filterDialog.restoreFilter(filterElement);
tableModel.setFilter(filterDialog.getFilter()); symbolModel.setFilter(filterDialog.getFilter());
} }
} }
@ -167,26 +186,26 @@ class SymbolPanel extends JPanel {
} }
private void handleTableSelection() { private void handleTableSelection() {
int selectedRowCount = symTable.getSelectedRowCount(); int selectedRowCount = gTable.getSelectedRowCount();
if (selectedRowCount == 1) { if (selectedRowCount == 1) {
int selectedRow = symTable.getSelectedRow(); int selectedRow = gTable.getSelectedRow();
Symbol symbol = symProvider.getSymbolForRow(selectedRow); Symbol symbol = symbolProvider.getSymbolForRow(selectedRow);
symProvider.setCurrentSymbol(symbol); // null allowed symbolProvider.setCurrentSymbol(symbol); // null allowed
} }
else { else {
symProvider.setCurrentSymbol(null); symbolProvider.setCurrentSymbol(null);
} }
} }
int getActualSymbolCount() { int getActualSymbolCount() {
return symTable.getRowCount(); return gTable.getRowCount();
} }
List<Symbol> getSelectedSymbols() { List<Symbol> getSelectedSymbols() {
List<Symbol> list = new ArrayList<>(); List<Symbol> list = new ArrayList<>();
int[] rows = symTable.getSelectedRows(); int[] rows = gTable.getSelectedRows();
for (SymbolRowObject rowObject : tableModel.getRowObjects(rows)) { for (SymbolRowObject rowObject : symbolModel.getRowObjects(rows)) {
Symbol s = rowObject.getSymbol(); Symbol s = rowObject.getSymbol();
if (s != null) { if (s != null) {
list.add(s); list.add(s);
@ -196,7 +215,7 @@ class SymbolPanel extends JPanel {
} }
GhidraTable getTable() { GhidraTable getTable() {
return symTable; return gTable;
} }
//================================================================================================== //==================================================================================================

View file

@ -33,6 +33,7 @@ import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
@ -45,6 +46,8 @@ class SymbolProvider extends ComponentProviderAdapter {
private SymbolTableModel symbolKeyModel; private SymbolTableModel symbolKeyModel;
private SymbolPanel symbolPanel; private SymbolPanel symbolPanel;
private boolean followIncomingLocationChanges;
SymbolProvider(SymbolTablePlugin plugin) { SymbolProvider(SymbolTablePlugin plugin) {
super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class); super(plugin.getTool(), "Symbol Table", plugin.getName(), ProgramActionContext.class);
this.plugin = plugin; this.plugin = plugin;
@ -63,6 +66,19 @@ class SymbolProvider extends ComponentProviderAdapter {
addToTool(); addToTool();
} }
void setFollowIncomingLocationChanges(boolean b) {
followIncomingLocationChanges = b;
}
void locationChanged(ProgramLocation location) {
if (!isVisible()) {
return;
}
if (followIncomingLocationChanges) {
symbolPanel.locationChanged(location);
}
}
void updateTitle() { void updateTitle() {
setSubTitle(generateSubTitle()); setSubTitle(generateSubTitle());
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,8 +15,6 @@
*/ */
package ghidra.app.plugin.core.symtable; package ghidra.app.plugin.core.symtable;
import java.util.Iterator;
import docking.widgets.table.DiscoverableTableUtils; import docking.widgets.table.DiscoverableTableUtils;
import docking.widgets.table.TableColumnDescriptor; import docking.widgets.table.TableColumnDescriptor;
import ghidra.app.services.BlockModelService; 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 INSTR_REFS_FROM = 1;
static final int DATA_REFS_FROM = 2; static final int DATA_REFS_FROM = 2;
private Symbol currentSymbol; private volatile Symbol currentSymbol;
private ReferenceManager refManager; private ReferenceManager refManager;
private int showRefMode = REFS_TO; private int showRefMode = REFS_TO;
private BlockModelService blockModelService; private BlockModelService blockModelService;
private boolean isDisposed;
SymbolReferenceModel(BlockModelService bms, PluginTool tool) { SymbolReferenceModel(BlockModelService bms, PluginTool tool) {
super("Symbol References", tool, null, null); super("Symbol References", tool, null, null);
@ -93,17 +90,11 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
int count = filteredData.size(); int count = filteredData.size();
description += count + " Reference"; description += count + " Reference";
if (count != 1) { if (count != 1) {
description += "s";//make plural... description += "s"; // make plural...
} }
return description; return description;
} }
@Override
public void dispose() {
isDisposed = true;
super.dispose();
}
@Override @Override
public void setProgram(Program prog) { public void setProgram(Program prog) {
if (isDisposed) { if (isDisposed) {
@ -145,9 +136,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
} }
private void checkRefs(Symbol symbol) { private void checkRefs(Symbol symbol) {
Iterator<Reference> iter = filteredData.iterator(); for (Reference ref : filteredData) {
while (iter.hasNext()) {
Reference ref = iter.next();
if (ref.getFromAddress().equals(symbol.getAddress())) { if (ref.getFromAddress().equals(symbol.getAddress())) {
reload(); reload();
return; return;
@ -173,6 +162,7 @@ public class SymbolReferenceModel extends AddressBasedTableModel<Reference> {
@Override @Override
protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor) protected void doLoad(Accumulator<Reference> accumulator, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
if (currentSymbol == null || getProgram() == null) { if (currentSymbol == null || getProgram() == null) {
return; return;
} }

View file

@ -20,11 +20,13 @@ import static ghidra.program.util.ProgramEvent.*;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import docking.ActionContext; import docking.ActionContext;
import docking.action.*; import docking.action.*;
import docking.action.builder.ActionBuilder;
import generic.theme.GIcon; import generic.theme.GIcon;
import ghidra.app.CorePluginPackage; import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramActivatedPluginEvent; 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.listing.Program;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramChangeRecord; import ghidra.program.util.ProgramChangeRecord;
import ghidra.program.util.ProgramLocation;
import ghidra.util.table.GhidraTable; import ghidra.util.table.GhidraTable;
import ghidra.util.table.SelectionNavigationAction; import ghidra.util.table.SelectionNavigationAction;
import ghidra.util.table.actions.MakeProgramSelectionAction; import ghidra.util.table.actions.MakeProgramSelectionAction;
@ -70,11 +73,14 @@ import resources.Icons;
"to show subsets of the symbols.", "to show subsets of the symbols.",
servicesRequired = { GoToService.class, BlockModelService.class }, servicesRequired = { GoToService.class, BlockModelService.class },
eventsProduced = { ProgramLocationPluginEvent.class }, eventsProduced = { ProgramLocationPluginEvent.class },
eventsConsumed = { ProgramActivatedPluginEvent.class } eventsConsumed = { ProgramActivatedPluginEvent.class, ProgramLocationPluginEvent.class }
) )
//@formatter:on //@formatter:on
public class SymbolTablePlugin extends Plugin { 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 WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_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 referencesToAction;
private ToggleDockingAction instructionsFromAction; private ToggleDockingAction instructionsFromAction;
private ToggleDockingAction dataFromAction; private ToggleDockingAction dataFromAction;
private ToggleDockingAction followIncomingAction;
private SelectionNavigationAction selectionNavigationAction;
private SymbolProvider symProvider; private SymbolProvider symProvider;
private ReferenceProvider refProvider; private ReferenceProvider refProvider;
@ -164,11 +172,21 @@ public class SymbolTablePlugin extends Plugin {
@Override @Override
public void readConfigState(SaveState saveState) { public void readConfigState(SaveState saveState) {
symProvider.readConfigState(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 @Override
public void writeConfigState(SaveState saveState) { public void writeConfigState(SaveState saveState) {
symProvider.writeConfigState(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 @Override
@ -194,6 +212,10 @@ public class SymbolTablePlugin extends Plugin {
tool.contextChanged(symProvider); tool.contextChanged(symProvider);
} }
else if (event instanceof ProgramLocationPluginEvent ple) {
ProgramLocation location = ple.getLocation();
symProvider.locationChanged(location);
}
} }
boolean isBusy() { boolean isBusy() {
@ -391,8 +413,22 @@ public class SymbolTablePlugin extends Plugin {
setFilterAction.setDescription("Configure Symbol Filter"); setFilterAction.setDescription("Configure Symbol Filter");
tool.addLocalAction(symProvider, setFilterAction); 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 // override the SelectionNavigationAction to handle both tables that this plugin uses
DockingAction selectionNavigationAction = selectionNavigationAction =
new SelectionNavigationAction(this, symProvider.getTable()) { new SelectionNavigationAction(this, symProvider.getTable()) {
@Override @Override
@ -401,6 +437,7 @@ public class SymbolTablePlugin extends Plugin {
refProvider.getTable().setNavigateOnSelectionEnabled(listen); refProvider.getTable().setNavigateOnSelectionEnabled(listen);
} }
}; };
selectionNavigationAction.getToolBarData().setToolBarGroup(navGroup);
tool.addLocalAction(symProvider, selectionNavigationAction); tool.addLocalAction(symProvider, selectionNavigationAction);
String pinnedPopupGroup = "2"; // second group String pinnedPopupGroup = "2"; // second group
@ -416,6 +453,7 @@ public class SymbolTablePlugin extends Plugin {
} }
private void createRefActions() { private void createRefActions() {
String toolbarGroup = "1";
referencesToAction = new ToggleDockingAction("References To", getName()) { referencesToAction = new ToggleDockingAction("References To", getName()) {
@Override @Override
public void actionPerformed(ActionContext context) { public void actionPerformed(ActionContext context) {
@ -435,7 +473,7 @@ public class SymbolTablePlugin extends Plugin {
referencesToAction.setDescription("References To"); referencesToAction.setDescription("References To");
referencesToAction.setSelected(true); referencesToAction.setSelected(true);
referencesToAction.setToolBarData( 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); tool.addLocalAction(refProvider, referencesToAction);
@ -458,7 +496,7 @@ public class SymbolTablePlugin extends Plugin {
instructionsFromAction.setDescription("Instructions From"); instructionsFromAction.setDescription("Instructions From");
instructionsFromAction.setSelected(false); instructionsFromAction.setSelected(false);
instructionsFromAction.setToolBarData( 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); tool.addLocalAction(refProvider, instructionsFromAction);
@ -481,9 +519,36 @@ public class SymbolTablePlugin extends Plugin {
dataFromAction.setDescription("Data From"); dataFromAction.setDescription("Data From");
dataFromAction.setSelected(false); dataFromAction.setSelected(false);
dataFromAction.setToolBarData( 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); 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 // 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.nav.NavigatableRemovalListener;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.HelpTopics; import ghidra.app.util.HelpTopics;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.Plugin; import ghidra.framework.plugintool.Plugin;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
@ -64,6 +65,9 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>(); private List<ComponentProviderActivationListener> activationListenerList = new ArrayList<>();
private Callback closedCallback = Dummy.callback(); private Callback closedCallback = Dummy.callback();
// optional client listener
private DomainObjectListener programListener;
private Navigatable navigatable; private Navigatable navigatable;
private SelectionNavigationAction selectionNavigationAction; private SelectionNavigationAction selectionNavigationAction;
private DockingAction selectAction; 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 @Override
public String getWindowSubMenuName() { public String getWindowSubMenuName() {
return windowSubMenu; return windowSubMenu;
@ -456,4 +469,22 @@ public class TableComponentProvider<T> extends ComponentProviderAdapter
public void setClosedCallback(Callback c) { public void setClosedCallback(Callback c) {
this.closedCallback = Dummy.ifNull(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

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,7 @@ package ghidra.app.util;
import java.util.Collection; import java.util.Collection;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils; import ghidra.app.plugin.core.navigation.locationreferences.ReferenceUtils;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
@ -36,8 +37,8 @@ public class FunctionXrefsTableModel extends ReferencesFromTableModel {
private Function function; private Function function;
private boolean showAllThunkXrefs; private boolean showAllThunkXrefs;
public FunctionXrefsTableModel(Function function, Collection<Reference> directRefs, ServiceProvider sp, public FunctionXrefsTableModel(Function function, Supplier<Collection<Reference>> directRefs,
Program program) { ServiceProvider sp, Program program) {
super(directRefs, sp, program); super(directRefs, sp, program);
this.function = function; this.function = function;

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,12 +15,28 @@
*/ */
package ghidra.app.util; 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.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.nav.Navigatable;
import ghidra.app.plugin.core.table.TableComponentProvider; import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.util.query.TableService; 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.framework.plugintool.ServiceProvider;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.data.DataUtilities; import ghidra.program.model.data.DataUtilities;
@ -30,16 +46,18 @@ import ghidra.program.util.*;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.table.ReferencesFromTableModel; import ghidra.util.table.ReferencesFromTableModel;
import ghidra.util.table.field.ReferenceEndpoint; import ghidra.util.table.field.ReferenceEndpoint;
import resources.Icons;
import resources.ResourceManager; import resources.ResourceManager;
public class XReferenceUtils { public class XReferenceUtils {
private static final String X_REFS_TO = "XRefs to "; private static final String X_REFS_TO = "XRefs to ";
// Methods in this class treat -1 as a key to return all references and // Methods in this class treat -1 as a key to return all references and not cap the result set.
// not cap the result set.
private final static int ALL_REFS = -1; private final static int ALL_REFS = -1;
private static OptionDialog promptToDeleteXrefsDialog;
/** /**
* Returns an array containing the first <b><code>max</code></b> * Returns an array containing the first <b><code>max</code></b>
* direct xref references to the specified code unit. * direct xref references to the specified code unit.
@ -69,13 +87,15 @@ public class XReferenceUtils {
} }
// Check for thunk reference // Check for thunk reference
Function func = program.getFunctionManager().getFunctionAt(minAddress); Function function = program.getFunctionManager().getFunctionAt(minAddress);
if (func != null) { if (function == null) {
Address[] thunkAddrs = func.getFunctionThunkAddresses(); return xrefs;
if (thunkAddrs != null) { }
for (Address thunkAddr : thunkAddrs) {
xrefs.add(new ThunkReference(thunkAddr, func.getEntryPoint())); Address[] thunkAddrs = function.getFunctionThunkAddresses(false);
} if (thunkAddrs != null) {
for (Address thunkAddr : thunkAddrs) {
xrefs.add(new ThunkReference(thunkAddr, function.getEntryPoint()));
} }
} }
return xrefs; return xrefs;
@ -203,10 +223,29 @@ public class XReferenceUtils {
* @param service the service needed to show the table * @param service the service needed to show the table
* @param location the location for which to find references * @param location the location for which to find references
* @param xrefs the xrefs to show * @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, public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
TableService service, ProgramLocation location, Collection<Reference> xrefs) { 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(); Address address = location.getAddress();
Program program = location.getProgram(); Program program = location.getProgram();
FunctionManager fm = program.getFunctionManager(); FunctionManager fm = program.getFunctionManager();
@ -223,25 +262,144 @@ public class XReferenceUtils {
String title = generateXRefTitle(location); String title = generateXRefTitle(location);
TableComponentProvider<ReferenceEndpoint> provider = TableComponentProvider<ReferenceEndpoint> provider =
service.showTable(title, "Xrefs", model, "Xrefs", navigatable); service.showTable(title, "Xrefs", model, "Xrefs", navigatable);
//
// Add Actions
//
provider.installRemoveItemsAction(); provider.installRemoveItemsAction();
if (function != null) { installRefreshAction(provider, model);
//@formatter:off
String actionName = "Show Thunk Xrefs";
new ToggleActionBuilder(actionName, provider.getActionOwner())
.toolBarIcon(ResourceManager.loadImage("images/ThunkFunction.gif"))
.toolBarGroup("A")
.helpLocation(new HelpLocation(HelpTopics.CODE_BROWSER, actionName))
.selected(false)
.onAction(c -> {
((FunctionXrefsTableModel) model).toggleShowAllThunkXRefs();
})
.buildAndInstallLocal(provider);
//@formatter:on
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())
.toolBarIcon(ResourceManager.loadImage("images/ThunkFunction.gif"))
.toolBarGroup("A")
.helpLocation(new HelpLocation(HelpTopics.CODE_BROWSER, actionName))
.selected(false)
.onAction(c -> {
((FunctionXrefsTableModel) model).toggleShowAllThunkXRefs();
})
.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; 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) { private static String generateXRefTitle(ProgramLocation location) {

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,8 +15,8 @@
*/ */
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import java.util.HashSet; import java.util.*;
import java.util.Set; import java.util.function.Supplier;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.util.XReferenceUtils; import ghidra.app.util.XReferenceUtils;
@ -39,7 +39,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
@Override @Override
protected Address getFromReferenceAddress(ProgramLocation programLocation) { protected Address getFromReferenceAddress(ProgramLocation programLocation) {
return ((VariableXRefFieldLocation) programLocation).getRefAddress(); return programLocation.getRefAddress();
} }
@Override @Override
@ -63,7 +63,7 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
VariableLocation variableLocation = (VariableLocation) location; VariableLocation variableLocation = (VariableLocation) location;
Variable variable = variableLocation.getVariable(); Variable variable = variableLocation.getVariable();
Set<Reference> refs = getVariableRefs(variable); Supplier<Collection<Reference>> refs = () -> getVariableRefs(variable);
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs); XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -16,7 +16,8 @@
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Set; import java.util.Collection;
import java.util.function.Supplier;
import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.field.*;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
@ -98,7 +99,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
} }
protected Address getFromReferenceAddress(ProgramLocation programLocation) { protected Address getFromReferenceAddress(ProgramLocation programLocation) {
return ((XRefFieldLocation) programLocation).getRefAddress(); return programLocation.getRefAddress();
} }
protected void showXRefDialog(Navigatable navigatable, ProgramLocation location, protected void showXRefDialog(Navigatable navigatable, ProgramLocation location,
@ -108,7 +109,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
return; return;
} }
Set<Reference> refs = XReferenceUtils.getAllXrefs(location); Supplier<Collection<Reference>> refs = () -> XReferenceUtils.getAllXrefs(location);
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs); XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
} }

View file

@ -43,7 +43,7 @@ import resources.Icons;
*/ */
public abstract class AbstractSelectionNavigationAction extends ToggleDockingAction { 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 static final String SELECTED_STATE = "SELECTION_NAVIGATION_SELECTED_STATE";
private SelectionListener selectionListener; 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" + 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.")); "in the program that corresponds to the selected row,\n as the selection changes."));
setHelpLocation(new HelpLocation("Search", "Selection_Navigation")); setHelpLocation(new HelpLocation("Search", "Selection_Navigation"));
setEnabled(true);
setSelected(true); // toggle button; enabled by default setSelected(true); // toggle button; enabled by default
initialize(); initialize();

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -18,8 +18,7 @@ package ghidra.util.table;
import java.awt.Color; import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -45,16 +44,21 @@ import ghidra.util.task.TaskMonitor;
*/ */
public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEndpoint> { public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEndpoint> {
private List<IncomingReferenceEndpoint> refs; private Supplier<Collection<Reference>> refsSupplier;
public ReferencesFromTableModel(Collection<Reference> refs, ServiceProvider sp, public ReferencesFromTableModel(Collection<Reference> refs, ServiceProvider sp,
Program program) { Program program) {
super("References", sp, program, null); super("References", sp, program, null);
this.refs = refs.stream().map(r -> { this.refsSupplier = () -> refs;
boolean offcut = ReferenceUtils.isOffcut(program, r.getToAddress());
return new IncomingReferenceEndpoint(r, offcut); addTableColumn(new ReferenceTypeTableColumn());
}).collect(Collectors.toList()); }
public ReferencesFromTableModel(Supplier<Collection<Reference>> refsSupplier,
ServiceProvider sp, Program program) {
super("References", sp, program, null);
this.refsSupplier = refsSupplier;
addTableColumn(new ReferenceTypeTableColumn()); addTableColumn(new ReferenceTypeTableColumn());
} }
@ -62,7 +66,13 @@ public class ReferencesFromTableModel extends AddressBasedTableModel<ReferenceEn
@Override @Override
protected void doLoad(Accumulator<ReferenceEndpoint> accumulator, TaskMonitor monitor) protected void doLoad(Accumulator<ReferenceEndpoint> accumulator, TaskMonitor monitor)
throws CancelledException { 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 @Override

View file

@ -112,7 +112,7 @@ public class DeleteTableRowAction extends DockingAction {
protected void removeSelectedItems() { protected void removeSelectedItems() {
TableModel model = table.getModel(); 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." + throw new AssertException("This action cannot delete rows for the given table model." +
"You can override this method to peform the delete action yourself."); "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)) { if (!(model instanceof ThreadedTableModel)) {
return false; return false;

View file

@ -16,6 +16,7 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;

View file

@ -16,12 +16,17 @@
package ghidra.app.plugin.core.navigation.locationreferences; package ghidra.app.plugin.core.navigation.locationreferences;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.*; import java.util.*;
import javax.swing.ListSelectionModel;
import org.junit.Test; import org.junit.Test;
import docking.action.DockingActionIf;
import docking.widgets.table.GTable;
import ghidra.app.cmd.function.SetReturnDataTypeCmd; import ghidra.app.cmd.function.SetReturnDataTypeCmd;
import ghidra.app.cmd.refs.RemoveReferenceCmd; import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.plugin.core.clear.ClearCmd; import ghidra.app.plugin.core.clear.ClearCmd;
@ -142,8 +147,8 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
// 0100415a - sscanf // 0100415a - sscanf
Address address = addr(0x0100415a); Address address = addr(0x0100415a);
int parameterColumn = 7; int returnTypeColumn = 7;
goTo(address, "Function Signature", parameterColumn); goTo(address, "Function Signature", returnTypeColumn);
search(); search();
@ -172,6 +177,33 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
verifyReferenceAddresses(address, referenceAddresses); 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 @Test
public void testLabelLocationDescriptor() throws Exception { public void testLabelLocationDescriptor() throws Exception {
@ -369,6 +401,19 @@ public class LocationReferencesPlugin3Test extends AbstractLocationReferencesTes
// Private Methods // 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 { private void createString_CallStructure(String addressString) throws Exception {
// String // String
// "call_structure_A: %s\n",00 // "call_structure_A: %s\n",00

View file

@ -122,8 +122,8 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
int row = findRow("ghidra"); int row = findRow("ghidra");
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
doubleClick(symbolTable, row, SymbolTableModel.LOCATION_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LOCATION_COL);
ProgramLocation pl = getProgramLocation(row, SymbolTableModel.LOCATION_COL, model); ProgramLocation pl = getProgramLocation(row, AbstractSymbolTableModel.LOCATION_COL, model);
assertEquals(pl.getAddress(), cbPlugin.getCurrentAddress()); assertEquals(pl.getAddress(), cbPlugin.getCurrentAddress());
} }
@ -131,7 +131,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingLabelColumn() throws Exception { public void testSortingLabelColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { 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)); assertTrue("row " + i + " not sorted correctly", (compare < 0 || compare == 0));
} }
sortDescending(SymbolTableModel.LABEL_COL); sortDescending(AbstractSymbolTableModel.LABEL_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -193,7 +193,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingAddressColumn() throws Exception { public void testSortingAddressColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
SymbolTableModel model = (SymbolTableModel) symbolTable.getModel(); SymbolTableModel model = (SymbolTableModel) symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -202,7 +202,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(loc1.compareTo(loc2) <= 0); assertTrue(loc1.compareTo(loc2) <= 0);
} }
sortDescending(SymbolTableModel.LOCATION_COL); sortDescending(AbstractSymbolTableModel.LOCATION_COL);
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
AddressBasedLocation loc1 = getLocation(i); AddressBasedLocation loc1 = getLocation(i);
@ -215,7 +215,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testSortingReferenceColumn() throws Exception { public void testSortingReferenceColumn() throws Exception {
openProgram("sample"); openProgram("sample");
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -224,7 +224,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertTrue(refs1.compareTo(refs2) <= 0); assertTrue(refs1.compareTo(refs2) <= 0);
} }
sortDescending(SymbolTableModel.REFS_COL); sortDescending(AbstractSymbolTableModel.REFS_COL);
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
Integer refs1 = getRefCount(i); Integer refs1 = getRefCount(i);
@ -325,12 +325,10 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
public void testEditing() throws Exception { public void testEditing() throws Exception {
openProgram("sample"); openProgram("sample");
waitForNotBusy();
String symbolName = "ghidra"; String symbolName = "ghidra";
int row = findRow(symbolName); int row = findRow(symbolName);
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
waitForSwing(); waitForSwing();
assertTrue(symbolTable.isEditing()); assertTrue(symbolTable.isEditing());
@ -361,7 +359,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
String symbolName = "ghidra"; String symbolName = "ghidra";
int row = findRow(symbolName); int row = findRow(symbolName);
doubleClick(symbolTable, row, SymbolTableModel.LABEL_COL); doubleClick(symbolTable, row, AbstractSymbolTableModel.LABEL_COL);
waitForSwing(); waitForSwing();
assertTrue(symbolTable.isEditing()); assertTrue(symbolTable.isEditing());
@ -782,6 +780,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
Address from = sample.getNewAddress(0x01004aea); Address from = sample.getNewAddress(0x01004aea);
Address to = sample.getNewAddress(0x50); Address to = sample.getNewAddress(0x50);
Reference ref = getReference(from, to); Reference ref = getReference(from, to);
assertNotNull(ref);
tx(program, () -> { tx(program, () -> {
ReferenceManager manager = program.getReferenceManager(); ReferenceManager manager = program.getReferenceManager();
@ -847,7 +846,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** LABEL **********************/ /************** LABEL **********************/
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
TableModel model = symbolTable.getModel(); TableModel model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -860,7 +859,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** ADDRESS **********************/ /************** ADDRESS **********************/
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -874,7 +873,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
/************** REFERENCES **********************/ /************** REFERENCES **********************/
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
model = symbolTable.getModel(); model = symbolTable.getModel();
for (int i = 0; i < model.getRowCount() - 1; ++i) { for (int i = 0; i < model.getRowCount() - 1; ++i) {
@ -893,7 +892,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
showReferencesTable(); 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); deleteText(textField);
// sort on a different column to trigger the other kind of filtering // sort on a different column to trigger the other kind of filtering
sortAscending(SymbolTableModel.REFS_COL); sortAscending(AbstractSymbolTableModel.REFS_COL);
text = "_"; text = "_";
myTypeText(textField, text); myTypeText(textField, text);
@ -1074,7 +1073,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
deleteText(textField); deleteText(textField);
// sort on a different column to trigger the other kind of filtering // sort on a different column to trigger the other kind of filtering
sortAscending(SymbolTableModel.LOCATION_COL); sortAscending(AbstractSymbolTableModel.LOCATION_COL);
text = "_"; text = "_";
myTypeText(textField, text); myTypeText(textField, text);
@ -1140,7 +1139,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
@Test @Test
public void testReferenceRemvoed_ReferenceToDynamicSymbol() throws Exception { public void testReferenceRemoved_ReferenceToDynamicSymbol() throws Exception {
openProgram("sample"); openProgram("sample");
@ -1153,10 +1152,63 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
assertFalse(row > -1); 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 // 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) { private Reference getReference(Address from, Address to) {
ReferenceManager rm = program.getReferenceManager(); 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; return null;
} }
@ -1234,6 +1285,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
return popup; return popup;
} }
private void selectRow(String symbolName) {
int row = findRow(symbolName);
selectRow(row);
}
private void selectRow(int row) { private void selectRow(int row) {
selectRows(row, row); selectRows(row, row);
@ -1243,18 +1299,22 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
} }
private void selectRows(int... rows) { private void selectRows(int... rows) {
selectRows(symbolTable, rows);
}
private void selectRows(JTable table, int... rows) {
assertNotNull(rows); assertNotNull(rows);
assertTrue("Must have at least one row to select", rows.length > 0); assertTrue("Must have at least one row to select", rows.length > 0);
runSwing(() -> { runSwing(() -> {
symbolTable.clearSelection(); table.clearSelection();
for (int row : rows) { for (int row : rows) {
symbolTable.addRowSelectionInterval(row, row); table.addRowSelectionInterval(row, row);
} }
int end = rows[rows.length - 1]; int end = rows[rows.length - 1];
Rectangle rect = symbolTable.getCellRect(end, 0, true); Rectangle rect = table.getCellRect(end, 0, true);
symbolTable.scrollRectToVisible(rect); table.scrollRectToVisible(rect);
}); });
waitForSwing(); waitForSwing();
} }
@ -1326,14 +1386,15 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
private Integer getRefCount(int row) { private Integer getRefCount(int row) {
Integer count = Integer count =
runSwing(() -> (Integer) symbolModel.getValueAt(row, SymbolTableModel.REFS_COL)); runSwing(
() -> (Integer) symbolModel.getValueAt(row, AbstractSymbolTableModel.REFS_COL));
return count; return count;
} }
private AddressBasedLocation getLocation(int row) { private AddressBasedLocation getLocation(int row) {
AddressBasedLocation location = AddressBasedLocation location =
runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row, runSwing(() -> (AddressBasedLocation) symbolModel.getValueAt(row,
SymbolTableModel.LOCATION_COL)); AbstractSymbolTableModel.LOCATION_COL));
return location; return location;
} }
@ -1650,16 +1711,17 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
waitForNotBusy(); waitForNotBusy();
sortAscending(SymbolTableModel.LABEL_COL); sortAscending(AbstractSymbolTableModel.LABEL_COL);
} }
private void showReferencesTable() { private void showReferencesTable() throws Exception {
performAction(viewRefAction, true); performAction(viewRefAction, true);
ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class); ReferenceProvider referencesProvider = waitForComponentProvider(ReferenceProvider.class);
referenceTable = referenceTable =
(GTable) findComponentByName(referencesProvider.getComponent(), "Reference Table"); (GTable) findComponentByName(referencesProvider.getComponent(), "Reference Table");
assertNotNull(referenceTable); assertNotNull(referenceTable);
waitForNotBusy();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -17,6 +17,9 @@ package ghidra.app.util.viewer.field;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTextField; import javax.swing.JTextField;
import org.junit.*; import org.junit.*;
@ -43,6 +46,7 @@ import ghidra.program.util.XRefHeaderFieldLocation;
import ghidra.test.AbstractGhidraHeadedIntegrationTest; import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv; import ghidra.test.TestEnv;
import ghidra.util.table.GhidraProgramTableModel; import ghidra.util.table.GhidraProgramTableModel;
import ghidra.util.table.GhidraTable;
/** /**
* Tests that references are displayed correctly when selecting the XRef field in * Tests that references are displayed correctly when selecting the XRef field in
@ -138,14 +142,14 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
/* /*
Direct References Direct References
01001009 ?? LAB_01001007 READ 01001009 ?? LAB_01001007 READ
01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL 01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL
01001050 thunk_FUN_01001005 ?? FUN_01001005 THUNK 01001050 thunk_FUN_01001005 ?? FUN_01001005 THUNK
References to the Thunk Function References to the Thunk Function
01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk
*/ */
@ -162,6 +166,27 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(3, model.getRowCount()); 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 @Test
public void testViewReferencesShowThunkXrefs_FromThunk() { public void testViewReferencesShowThunkXrefs_FromThunk() {
@ -176,12 +201,12 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
/* /*
Direct References Direct References
01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk
References to the thunk and the end thunked function References to the thunk and the end thunked function
01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk 01001046 ?? thunk_FUN_01001005 UNCONDITIONAL_CALL thunk
01001009 ?? LAB_01001007 READ 01001009 ?? LAB_01001007 READ
01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL 01001050 thunk_FUN_01001005 ?? FUN_01001005 UNCONDITIONAL_CALL
@ -218,6 +243,42 @@ public class XrefViewerTest extends AbstractGhidraHeadedIntegrationTest {
// Private Methods // 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) { private void createThunkFunction(String thunkAddressString, String thunkedAddressString) {
Address thunkAddress = addr(thunkAddressString); Address thunkAddress = addr(thunkAddressString);

View file

@ -86,7 +86,10 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
private GTree inTree; private GTree inTree;
private GTree outTree; private GTree outTree;
private boolean isPrimary; private boolean isPrimary;
private enum Condition {IN, OUT, EITHER}
private enum Condition {
IN, OUT, EITHER
}
private SwingUpdateManager reloadUpdateManager = new SwingUpdateManager(500, () -> doUpdate()); private SwingUpdateManager reloadUpdateManager = new SwingUpdateManager(500, () -> doUpdate());
@ -286,12 +289,13 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
// //
// navigate incoming nodes on selection // 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" + .description(HTMLUtilities.toHTML("Incoming Navigation" +
"<br><br>Toggle <b>On</b> - change the displayed " + "<br><br>Toggle <b>On</b> - change the displayed " +
"function on Listing navigation events" + "function on Listing navigation events" +
"<br>Toggled <b>Off</b> - don't 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")) .helpLocation(new HelpLocation(ownerName, "Call_Tree_Action_Incoming_Navigation"))
.sharedKeyBinding()
.toolBarGroup(navigationOptionsToolbarGroup, "2") .toolBarGroup(navigationOptionsToolbarGroup, "2")
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.selected(isPrimary) .selected(isPrimary)
@ -305,7 +309,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
//@formatter:on //@formatter:on
} }
private boolean isValidSelection(ActionContext context) { private boolean isValidSelection(ActionContext context) {
Object contextObject = context.getContextObject(); Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject; GTree gTree = (GTree) contextObject;
@ -333,7 +336,7 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
if (contextObject == outTree && cond == Condition.IN) { if (contextObject == outTree && cond == Condition.IN) {
return false; return false;
} }
return true; return true;
} }
@ -348,7 +351,7 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
if (currentFunction == null) { if (currentFunction == null) {
return false; return false;
} }
Object contextObject = context.getContextObject(); Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject; GTree gTree = (GTree) contextObject;
TreePath[] selectionPaths = gTree.getSelectionPaths(); TreePath[] selectionPaths = gTree.getSelectionPaths();
@ -390,7 +393,6 @@ public class TaintSliceTreeProvider extends ComponentProviderAdapter
return isValidContextMult(context, Condition.OUT); return isValidContextMult(context, Condition.OUT);
} }
private void expandToDepth(ActionContext context) { private void expandToDepth(ActionContext context) {
Object contextObject = context.getContextObject(); Object contextObject = context.getContextObject();
GTree gTree = (GTree) contextObject; GTree gTree = (GTree) contextObject;

View file

@ -112,12 +112,15 @@ public class ComponentInfoDialog extends DialogComponentProvider implements Prop
.build(); .build();
addAction(eventAction); addAction(eventAction);
toggleFollowFocusAction = new ToggleActionBuilder("Follow Focus", ACTION_OWNER) toggleFollowFocusAction =
.toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON) new ToggleActionBuilder("Navigate on Incoming Location Changes", ACTION_OWNER)
.description("On causes component table to constant repopulate as focus changes") .sharedKeyBinding()
.onAction(c -> toggleFollowFocus()) .toolBarIcon(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON)
.selected(true) .description(
.build(); "On causes component table to constant repopulate as focus changes")
.onAction(c -> toggleFollowFocus())
.selected(true)
.build();
addAction(toggleFollowFocusAction); addAction(toggleFollowFocusAction);
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -36,17 +36,17 @@ public interface Reference extends Comparable<Reference> {
public static final int OTHER = -2; 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(); public Address getFromAddress();
/** /**
* Get the "to" address for this reference. * {@return Get the "to" address for this reference.}
*/ */
public Address getToAddress(); public Address getToAddress();
/** /**
* Return whether this reference is marked as primary. * {@return Return whether this reference is marked as primary.}
*/ */
public boolean isPrimary(); public boolean isPrimary();
@ -59,7 +59,7 @@ public interface Reference extends Comparable<Reference> {
public long getSymbolID(); public long getSymbolID();
/** /**
* Get the type of reference being made. * {@return Get the type of reference being made.}
*/ */
public RefType getReferenceType(); public RefType getReferenceType();
@ -71,50 +71,50 @@ public interface Reference extends Comparable<Reference> {
public int getOperandIndex(); 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(); 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(); public boolean isOperandReference();
/** /**
* Returns true if this reference is an instance of StackReference and * {@return Returns true if this reference is an instance of StackReference and
* refers to a stack location. * refers to a stack location.}
*/ */
public boolean isStackReference(); 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(); 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(); public boolean isEntryPointReference();
/** /**
* Returns true if this reference to an address in the programs memory * {@return Returns true if this reference to an address in the programs memory
* space. This includes offset and shifted references. * space. This includes offset and shifted references.}
*/ */
public boolean isMemoryReference(); public boolean isMemoryReference();
/** /**
* Returns true if this reference to an address in the programs register * {@return Returns true if this reference to an address in the programs register
* space. * space.}
*/ */
public boolean isRegisterReference(); 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(); 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(); public boolean isShiftedReference();

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -72,7 +72,7 @@ public interface ReferenceManager {
* an operand will be made primary by default. All non-memory references * an operand will be made primary by default. All non-memory references
* will be removed from the specified operand. Certain reference {@link RefType types} * will be removed from the specified operand. Certain reference {@link RefType types}
* may not be specified (e.g., {@link RefType#FALL_THROUGH}). * 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. * @param toAddr address of the location being referenced.
* Memory, stack, and register addresses are all permitted. * Memory, stack, and register addresses are all permitted.
* @param type reference type - how the location is being referenced. * @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 source the source of this reference
* @param type reference type - how the location is being referenced * @param type reference type - how the location is being referenced
* @return external reference * @return external reference
* @throws InvalidInputException * @throws InvalidInputException if the input is invalid
*/ */
public Reference addExternalReference(Address fromAddr, int opIndex, ExternalLocation location, public Reference addExternalReference(Address fromAddr, int opIndex, ExternalLocation location,
SourceType source, RefType type) throws InvalidInputException; SourceType source, RefType type) throws InvalidInputException;
@ -204,7 +204,7 @@ public interface ReferenceManager {
/** /**
* Remove all stack, external, and memory references for the given * Remove all stack, external, and memory references for the given
* from address. * 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); public void removeAllReferencesFrom(Address fromAddr);
@ -247,7 +247,7 @@ public interface ReferenceManager {
/** /**
* Get all flow references from the given address. * 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. * @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 * Get the reference that has the given from and to address, and
* operand index. * 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 toAddr the address being referred to.
* @param opIndex the operand index. * @param opIndex the operand index.
* @return reference which satisfies the specified criteria or null * @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 * address/opIndex. Keep in mind this is a rather inefficient
* method as it must examine all references from the specified * method as it must examine all references from the specified
* fromAddr. * 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. * @param opIndex the index of the operand being tested.
* @return true if one or more reference from the specified address * @return true if one or more reference from the specified address
* and opindex are defined, else false * and opindex are defined, else false
@ -316,7 +316,7 @@ public interface ReferenceManager {
/** /**
* Returns true if there are any memory references at the given * Returns true if there are any memory references at the given
* address. * 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 * @return true if one or more reference from the specified address
* are defined, else false * are defined, else false
*/ */
@ -378,18 +378,20 @@ public interface ReferenceManager {
/** /**
* Returns the number of references from the specified <code>fromAddr</code>. * 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>. * @return the number of references from the specified <code>fromAddr</code>.
*/ */
public int getReferenceCountFrom(Address fromAddr); public int getReferenceCountFrom(Address fromAddr);
/** /**
* Return the number of references for "to" addresses. * Return the number of references for "to" addresses.
* @return the number of references for "to" addresses.
*/ */
public int getReferenceDestinationCount(); public int getReferenceDestinationCount();
/** /**
* Return the number of references for "from" addresses. * Return the number of references for "from" addresses.
* @return the number of references for "from" addresses.
*/ */
public int getReferenceSourceCount(); public int getReferenceSourceCount();
@ -401,7 +403,7 @@ public interface ReferenceManager {
public boolean hasReferencesTo(Address toAddr); 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 ref reference to be updated
* @param refType new reference type * @param refType new reference type
* @return updated reference * @return updated reference