Merge remote-tracking branch 'origin/GP-6430-dragonmacher-delete-refs--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-03-02 04:43:21 -05:00
11 changed files with 204 additions and 55 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 44 KiB

@@ -165,7 +165,7 @@
</BLOCKQUOTE>
<H2><A name="Delete_Reference"></A>Delete Reference<IMG src="Icons.DELETE_ICON"></H2>
<H2><A name="Delete_References"></A>Delete Reference<IMG src="Icons.DELETE_ICON"></H2>
<BLOCKQUOTE>
<P>This action will delete all selected references from the database.
@@ -145,7 +145,7 @@
<LI>Select the symbols in the Symbol Table (hold the &lt;Ctrl&gt; key down to add to the
selection) to be deleted.</LI>
<LI>Right-mouse-click and select "Delete" from the popup menu, or click the <IMG src=
<LI>Right-mouse-click and select <B>Delete</B> from the popup menu, or click the <IMG src=
"images/edit-delete.png"> button in the <I>Symbol Table</I> toolbar.</LI>
</OL>
@@ -153,7 +153,10 @@
<P><I><IMG src="help/shared/note.png"> Notes on deleting a symbol:</I></P>
<OL>
<LI><I>You can only delete a default symbol when it has zero (0) references.</I></LI>
<LI><I>You can only delete a default symbol when it has zero (0) references. Use the
<B>Delete All References</B> action (described below) to remove all references. Once
all references to a default symbol are removed, the symbol will be removed.
</I></LI>
<LI><I>If you delete a user-defined symbol with references, then a default symbol will
automatically be created and assigned those references.</I></LI>
@@ -163,6 +166,22 @@
</OL>
</BLOCKQUOTE>
</BLOCKQUOTE>
<H2><A name="Delete_All_References"></A>Deleting Symbol References</H2>
<BLOCKQUOTE>
<P>You can use the <I>Symbol Table</I> to delete all referencs for a given symbol(s).
<P>To delete symbol references:</P>
<OL>
<LI>Select the symbols in the Symbol Table (hold the &lt;Ctrl&gt; key down to add to the
selection) to be deleted.</LI>
<LI>Right-mouse-click and select <B>References-&gt;Delete All</B> from the popup menu.</LI>
</OL>
</BLOCKQUOTE>
<H2><A name="Make_Selection"></A>Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
@@ -190,7 +209,7 @@
<H2><A name="Navigate_on_Incoming_Location_Changes"></A>
Making a Selection <IMG src="Icons.MAKE_SELECTION_ICON"></H2>
Navigate on Incoming Location Changes<IMG src="Icons.NAVIGATE_ON_INCOMING_EVENT_ICON"></H2>
<BLOCKQUOTE>
<P>When selected, the Symbol Table will select the row in the table that corresponds to the
@@ -198,6 +217,15 @@
</P>
</BLOCKQUOTE>
<H2><A name="Navigate_on_Row_Changes"></A>
Navigate on Table Row Changes<IMG src="Icons.NAVIGATE_ON_OUTGOING_EVENT_ICON"></H2>
<BLOCKQUOTE>
<P>When toggle on, table row selections in the Symbol Table cause the
<A href="help/topics/CodeBrowserPlugin/CodeBrowser.htm">Listing</A> to go to the
selected address.</P>
</BLOCKQUOTE>
<H2>Renaming a Symbol</H2>
@@ -619,8 +647,8 @@
<P class="relatedtopic">Related Topics</P>
<UL>
<LI><A href="symbol_references.htm">Symbol References</A></LI>
<LI><A href="help/topics/LabelMgrPlugin/Labels.htm">Labels</A></LI>
<LI><A href="help/topics/CodeBrowserPlugin/CodeBrowserOptions.htm#Listing_Display">Listing
Display Options</A></LI>
</UL>
@@ -4,9 +4,9 @@
* 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.
@@ -63,7 +63,7 @@ public class RemoveReferenceCmd implements Command<Program> {
return true;
}
status = "Reference not found";
status = "Reference not found - from: %s; to: %s ".formatted(fromAddr, toAddr);
return false;
}
@@ -4,9 +4,9 @@
* 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.
@@ -47,8 +47,13 @@ class ReferencePanel extends JPanel {
refTable = threadedTablePanel.getTable();
refTable.setAutoLookupColumn(SymbolReferenceModel.LABEL_COL);
refTable.setPreferredScrollableViewportSize(new Dimension(250, 200));
refTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
refTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
refTable.installNavigation(provider.getTool());
refTable.getSelectionModel().addListSelectionListener(e -> {
if (!e.getValueIsAdjusting()) {
provider.contextChanged();
}
});
this.listener = e -> referenceProvider.updateTitle();
refTable.getModel().addTableModelListener(listener);
@@ -18,8 +18,10 @@ package ghidra.app.plugin.core.symtable;
import static ghidra.framework.model.DomainObjectEvent.*;
import static ghidra.program.util.ProgramEvent.*;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
@@ -27,8 +29,12 @@ import javax.swing.Icon;
import docking.ActionContext;
import docking.action.*;
import docking.action.builder.ActionBuilder;
import docking.widgets.OptionDialog;
import docking.widgets.OptionDialogBuilder;
import generic.theme.GIcon;
import ghidra.app.CorePluginPackage;
import ghidra.app.cmd.refs.RemoveReferenceCmd;
import ghidra.app.context.ProgramSymbolActionContext;
import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.events.ProgramLocationPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
@@ -47,11 +53,13 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*;
import ghidra.program.util.ProgramChangeRecord;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.SelectionNavigationAction;
import ghidra.util.table.actions.MakeProgramSelectionAction;
import ghidra.util.task.SwingUpdateManager;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.*;
import ghidra.util.worker.Job;
import ghidra.util.worker.Worker;
import resources.Icons;
@@ -84,10 +92,6 @@ public class SymbolTablePlugin extends Plugin {
final static Cursor WAIT_CURSOR = new Cursor(Cursor.WAIT_CURSOR);
final static Cursor NORM_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
private DockingAction openRefsAction;
private DockingAction deleteAction;
private DockingAction makeSelectionAction;
private DockingAction setFilterAction;
private ToggleDockingAction referencesToAction;
private ToggleDockingAction instructionsFromAction;
private ToggleDockingAction dataFromAction;
@@ -144,9 +148,6 @@ public class SymbolTablePlugin extends Plugin {
super.dispose();
swingMgr.dispose();
deleteAction.dispose();
makeSelectionAction.dispose();
domainObjectWorker.dispose();
if (symProvider != null) {
symProvider.dispose();
@@ -345,13 +346,14 @@ public class SymbolTablePlugin extends Plugin {
private void createSymActions() {
String popupGroup = "1";
openRefsAction = new DockingAction("Symbol References", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
refProvider.open();
refProvider.setCurrentSymbol(symProvider.getCurrentSymbol());
}
};
DockingAction openRefsAction =
new DockingAction("Symbol References", getName(), KeyBindingType.SHARED) {
@Override
public void actionPerformed(ActionContext context) {
refProvider.open();
refProvider.setCurrentSymbol(symProvider.getCurrentSymbol());
}
};
Icon icon = new GIcon("icon.plugin.symboltable.referencetable.provider");
openRefsAction.setPopupMenuData(
new MenuData(new String[] { "Symbol References" }, icon, popupGroup));
@@ -360,7 +362,7 @@ public class SymbolTablePlugin extends Plugin {
openRefsAction.setDescription("Display Symbol References");
tool.addLocalAction(symProvider, openRefsAction);
deleteAction = new DockingAction("Delete Symbols", getName()) {
DockingAction deleteAction = new DockingAction("Delete Symbols", getName()) {
@Override
public void actionPerformed(ActionContext context) {
symProvider.deleteSymbols();
@@ -385,18 +387,23 @@ public class SymbolTablePlugin extends Plugin {
deleteAction.setKeyBindingData(new KeyBindingData(KeyEvent.VK_DELETE, 0));
deleteAction.setDescription("Delete Selected Symbols");
deleteAction.setEnabled(false);
tool.addLocalAction(symProvider, deleteAction);
DockingAction editExternalLocationAction = new EditExternalLocationAction(this);
tool.addLocalAction(symProvider, editExternalLocationAction);
makeSelectionAction = new MakeProgramSelectionAction(this, symProvider.getTable());
makeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup);
MakeProgramSelectionAction symbolMakeSelectionAction =
new MakeProgramSelectionAction(this, symProvider.getTable());
symbolMakeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup);
tool.addLocalAction(symProvider, makeSelectionAction);
MakeProgramSelectionAction refsMakeSelectionAction =
new MakeProgramSelectionAction(this, refProvider.getTable());
refsMakeSelectionAction.getPopupMenuData().setMenuGroup(popupGroup);
setFilterAction = new DockingAction("Set Filter", getName()) {
tool.addLocalAction(symProvider, symbolMakeSelectionAction);
tool.addLocalAction(refProvider, refsMakeSelectionAction);
DockingAction setFilterAction = new DockingAction("Set Filter", getName()) {
@Override
public void actionPerformed(ActionContext context) {
symProvider.setFilter();
@@ -438,6 +445,8 @@ public class SymbolTablePlugin extends Plugin {
}
};
selectionNavigationAction.getToolBarData().setToolBarGroup(navGroup);
selectionNavigationAction.setHelpLocation(new HelpLocation(getName(),
"Navigate_on_Row_Changes"));
tool.addLocalAction(symProvider, selectionNavigationAction);
String pinnedPopupGroup = "2"; // second group
@@ -450,6 +459,81 @@ public class SymbolTablePlugin extends Plugin {
CreateSymbolTableAction tableAction = new CreateSymbolTableAction(this);
tableAction.getPopupMenuData().setMenuGroup(popupGroup);
tool.addLocalAction(symProvider, tableAction);
//@formatter:off
String bottomGroup = "ShowReferencesTo" + 1;
new ActionBuilder("Delete All References", getName())
.sharedKeyBinding()
.popupMenuIcon(Icons.DELETE_ICON)
.popupMenuPath("References", "Delete All")
.popupMenuGroup(bottomGroup)
.withContext(ProgramSymbolActionContext.class)
.enabledWhen(this::hasSymbolsSelected)
.onAction(this::deleteRefs)
.buildAndInstallLocal(symProvider);
//@formatter:on
}
private boolean hasSymbolsSelected(ProgramSymbolActionContext c) {
if (symProvider.isBusy()) {
return false;
}
Component source = c.getSourceComponent();
if (source != symProvider.getTable()) {
return false;
}
return c.getSymbolCount() != 0;
}
private void deleteRefs(ProgramSymbolActionContext c) {
int result = new OptionDialogBuilder("Delete References?",
"Delete all references to the selected symbol(s)?")
.addOption("Delete")
.addCancel()
.show(symProvider.getComponent());
if (result != OptionDialog.OPTION_ONE) {
return;
}
int n = c.getSymbolCount();
Iterable<Symbol> it = c.getSymbols();
TaskLauncher.launchModal("Deleting References", monitor -> {
try {
doDeleteRefs(it, n, monitor);
}
catch (CancelledException e) {
// don't care
}
});
}
private void doDeleteRefs(Iterable<Symbol> it, int n, TaskMonitor monitor)
throws CancelledException {
monitor.initialize(n, "Gathering references...");
List<RemoveReferenceCmd> commands = new ArrayList<>();
for (Symbol s : it) {
monitor.increment();
Reference[] refs = s.getReferences();
for (Reference ref : refs) {
RemoveReferenceCmd cmd = new RemoveReferenceCmd(ref);
commands.add(cmd);
}
}
monitor.initialize(n, "Deleting references...");
currentProgram.withTransaction("Deleting References", () -> {
for (RemoveReferenceCmd cmd : commands) {
monitor.increment();
if (!cmd.applyTo(currentProgram)) {
Msg.error(this, "Failed to delete reference: " + cmd.getStatusMsg());
}
}
});
}
private void createRefActions() {
@@ -525,26 +609,21 @@ public class SymbolTablePlugin extends Plugin {
//@formatter:off
toolbarGroup = "2";
String actionName = "Delete Reference";
new ActionBuilder(actionName, getName())
new ActionBuilder("Delete References", getName())
.sharedKeyBinding()
.toolBarIcon(Icons.DELETE_ICON)
.toolBarGroup(toolbarGroup)
.withContext(ReferenceTableContext.class)
.enabledWhen(c -> {
if (!(c instanceof ReferenceTableContext context)) {
return false;
}
if (refProvider.isBusy()) {
return false;
}
List<Reference> refs = context.getSelectedReferences();
List<Reference> refs = c.getSelectedReferences();
return !refs.isEmpty();
})
.onAction(c -> {
ReferenceTableContext context = (ReferenceTableContext) c;
List<Reference> refs = context.getSelectedReferences();
List<Reference> refs = c.getSelectedReferences();
refProvider.deleteRows(refs);
})
.buildAndInstallLocal(refProvider);
@@ -63,8 +63,8 @@ import ghidra.app.util.viewer.field.AddressFieldFactory;
import ghidra.app.util.viewer.field.FieldFactory;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.app.util.viewer.listingpanel.ListingMarginProvider;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.framework.ToolUtils;
import ghidra.framework.plugintool.Plugin;
@@ -692,6 +692,7 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
Assert.assertNotNull("Did not find a dialog to capture for class: " + clazz,
dialogProvider);
JDialog dialog = (JDialog) getInstanceField("dialog", dialogProvider);
dialog.toFront();
waitForSwing();
paintFix(dialog);
runSwing(() -> generateImage(dialog));
@@ -702,7 +703,7 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
Assert.assertNotNull("Dialog cannot be null", provider);
JDialog dialog = (JDialog) getInstanceField("dialog", provider);
dialog.toFront();
paintFix(dialog);
runSwing(() -> generateImage(dialog));
@@ -105,7 +105,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
viewRefAction = CollectionUtils.any(symbolReferencesActions);
deleteAction = getAction(plugin, "Delete Symbols");
makeSelectionAction = getAction(plugin, "Make Selection");
makeSelectionAction = getLocalAction(provider, "Make Selection");
setFilterAction = getAction(plugin, "Set Filter");
setPinnedAction = getAction(plugin, "Pin Symbol");
clearPinnedAction = getAction(plugin, "Clear Pinned Symbol");
@@ -481,6 +481,46 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
plugin.getSymbolProvider().getName() + " " + plugin.getSymbolProvider().getSubTitle());
}
private void assertEnabled(DockingActionIf action, boolean expected) {
ActionContext context = runSwing(() -> provider.getActionContext(null));
boolean actual = runSwing(() -> action.isEnabledForContext(context));
if (expected) {
assertTrue("Action should have been enabled: " + action.getName(), actual);
}
else {
assertFalse("Action should not have been enabled: " + action.getName(), actual);
}
}
@Test
public void testDeleteAll() throws Exception {
openProgram("sample");
//
// Test that we can delete all references to a symbol from the symbol table.
//
DockingActionIf deleteAllAction = getAction(plugin, "Delete All References");
int selectedRows = symbolTable.getSelectedRowCount();
assertEquals(0, selectedRows);
assertEnabled(deleteAllAction, false);
// Select a symbol with references
int row = findRow("ghidra");
Rectangle rect = symbolTable.getCellRect(row, 0, true);
symbolTable.scrollRectToVisible(rect);
singleClick(symbolTable, row, 0);
assertEnabled(deleteAllAction, true);
Symbol symbol = getSymbol(row);
int startRefCount = symbol.getReferenceCount();
assertTrue(startRefCount > 0);
performAction(deleteAllAction, provider, true);
assertEquals(0, symbol.getReferenceCount());
}
@Test
public void testDeleteParamter_ForSCR_7892() throws Exception {
openProgram("login");
@@ -1731,11 +1771,11 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
return (GhidraTableFilterPanel<Symbol>) getInstanceField("tableFilterPanel", panel);
}
private void singleClick(final JTable table, final int row, final int col) throws Exception {
private void singleClick(final JTable table, final int row, final int col) {
clickTableCell(table, row, col, 1);
}
private void doubleClick(final JTable table, final int row, final int col) throws Exception {
private void doubleClick(final JTable table, final int row, final int col) {
clickTableCell(table, row, col, 2);
}
@@ -4,9 +4,9 @@
* 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.
@@ -49,7 +49,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
GTable table = getTable(symbolProvider);
setColumnSizes(table);
selectRow(table, "WideCharToMultiByte");
triggerText(table, "\n"); // hack to kick the references table
Window window = windowForComponent(symbolProvider.getComponent());
captureWindow(window);
@@ -67,7 +66,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
GTable table = getTable(symbolProvider);
setColumnSizes(table);
selectRow(table, "_malloc00403762");
triggerText(table, "\n"); // hack to kick the references table
Window window = windowForComponent(symbolProvider.getComponent());
captureWindow(window);
@@ -85,7 +83,6 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
GTable table = getTable(symbolProvider);
setColumnSizes(table);
selectRow(table, "FUN_004010e0");
triggerText(table, "\n"); // hack to kick the references table
Window window = windowForComponent(symbolProvider.getComponent());
captureWindow(window);
@@ -124,8 +121,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
performAction("Set Filter", "SymbolTablePlugin", false);
FilterDialog dialog =
waitForDialogComponent(null, FilterDialog.class, DEFAULT_WINDOW_TIMEOUT);
FilterDialog dialog = waitForDialogComponent(FilterDialog.class);
final JCheckBox advancedCheckBox =
(JCheckBox) getInstanceField("advancedFilterCheckbox", dialog);
@@ -160,7 +156,7 @@ public class SymbolTablePluginScreenShots extends GhidraScreenShotGenerator {
private GTable getTable(ComponentProvider provider) {
Object symbolPanel = getInstanceField("symbolPanel", provider);
return (GTable) getInstanceField("symTable", symbolPanel);
return (GTable) getInstanceField("gTable", symbolPanel);
}
private void setColumnSizes(final GTable table) {