GP-6783: Use record instead of Object[] in get/restoreTransientState. Fix Debugger NPE.

This commit is contained in:
Dan
2026-05-11 16:47:13 +00:00
parent 9b9cb62ca1
commit 4446724bf7
10 changed files with 201 additions and 183 deletions
@@ -15,7 +15,7 @@
*/
package ghidra.app.plugin.core.bookmark;
import static ghidra.framework.model.DomainObjectEvent.*;
import static ghidra.framework.model.DomainObjectEvent.RESTORED;
import static ghidra.program.util.ProgramEvent.*;
import java.awt.event.KeyEvent;
@@ -34,13 +34,13 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.events.ProgramSelectionPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.bookmark.BookmarkPlugin.BookmarkTransientState;
import ghidra.app.services.*;
import ghidra.framework.cmd.CompoundCmd;
import ghidra.framework.model.DomainObjectListener;
import ghidra.framework.model.DomainObjectListenerBuilder;
import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.PluginInfo;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
@@ -68,7 +68,8 @@ import resources.MultiIconBuilder;
eventsProduced = { ProgramSelectionPluginEvent.class }
)
//@formatter:on
public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider, BookmarkService {
public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider, BookmarkService,
PluginWithTransientState<BookmarkTransientState> {
private final static int MAX_DELETE_ACTIONS = 10;
@@ -392,14 +393,16 @@ public class BookmarkPlugin extends ProgramPlugin implements PopupActionProvider
bookmarkMgr = null;
}
record BookmarkTransientState(FilterState filterState) {}
@Override
public Object getTransientState() {
return provider.getFilterState();
public BookmarkTransientState getTransientState() {
return new BookmarkTransientState(provider.getFilterState());
}
@Override
public void restoreTransientState(Object state) {
provider.restoreFilterState((FilterState) state);
public void restoreTransientState(BookmarkTransientState state) {
provider.restoreFilterState(state.filterState);
}
@Override
@@ -26,6 +26,7 @@ import ghidra.app.CorePluginPackage;
import ghidra.app.context.*;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin.CodeBrowserTransientState;
import ghidra.app.services.*;
import ghidra.app.util.viewer.format.FormatManager;
import ghidra.app.util.viewer.listingpanel.ListingPanel;
@@ -63,7 +64,8 @@ import ghidra.program.util.ProgramSelection;
ViewChangedPluginEvent.class, ProgramHighlightPluginEvent.class },
eventsProduced = { ProgramLocationPluginEvent.class, ProgramSelectionPluginEvent.class })
//@formatter:on
public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvider> {
public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvider>
implements PluginWithTransientState<CodeBrowserTransientState> {
public CodeBrowserPlugin(PluginTool tool) {
super(tool);
@@ -181,38 +183,28 @@ public class CodeBrowserPlugin extends AbstractCodeBrowserPlugin<CodeViewerProvi
}
}
record CodeBrowserTransientState(ViewerPosition position, ProgramLocation location,
ProgramSelection highlight, ProgramSelection selection, AddressSetView view) {}
@Override
public Object getTransientState() {
Object[] state = new Object[5];
public CodeBrowserTransientState getTransientState() {
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
state[0] = fieldPanel.getViewerPosition();
state[1] = connectedProvider.getLocation();
state[2] = connectedProvider.getHighlight();
state[3] = connectedProvider.getSelection();
state[4] = currentView;
return state;
return new CodeBrowserTransientState(fieldPanel.getViewerPosition(),
connectedProvider.getLocation(), connectedProvider.getHighlight(),
connectedProvider.getSelection(), currentView);
}
@Override
public void restoreTransientState(final Object objectState) {
Object[] state = (Object[]) objectState;
ViewerPosition vp = (ViewerPosition) state[0];
ProgramLocation location = (ProgramLocation) state[1];
ProgramSelection highlight = (ProgramSelection) state[2];
ProgramSelection selection = (ProgramSelection) state[3];
setView((AddressSetView) state[4]);
if (location != null) {
connectedProvider.setLocation(location);
public void restoreTransientState(final CodeBrowserTransientState state) {
setView(state.view);
if (state.location != null) {
connectedProvider.setLocation(state.location);
}
connectedProvider.setHighlight(highlight);
if (selection != null) {
connectedProvider.setSelection(selection);
connectedProvider.setHighlight(state.highlight);
if (state.selection != null) {
connectedProvider.setSelection(state.selection);
}
ViewerPosition vp = state.position;
if (vp != null) {
FieldPanel fieldPanel = connectedProvider.getListingPanel().getFieldPanel();
fieldPanel.setViewerPosition(vp.getIndex(), vp.getXOffset(), vp.getYOffset());
@@ -30,6 +30,7 @@ import ghidra.app.events.ProgramActivatedPluginEvent;
import ghidra.app.events.TreeSelectionPluginEvent;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.ProgramPlugin;
import ghidra.app.plugin.core.programtree.ProgramTreePlugin.ProgramTreeTransientState;
import ghidra.app.services.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.options.*;
@@ -48,8 +49,8 @@ import ghidra.util.task.RunManager;
import resources.Icons;
/**
* Plugin that creates view provider services to show the trees in a program.
* Notifies the view manager service when the view changes.
* Plugin that creates view provider services to show the trees in a program. Notifies the view
* manager service when the view changes.
*/
//@formatter:off
@PluginInfo(
@@ -68,7 +69,8 @@ import resources.Icons;
)
//@formatter:on
public class ProgramTreePlugin extends ProgramPlugin
implements ProgramTreeService, OptionsChangeListener {
implements ProgramTreeService, OptionsChangeListener,
PluginWithTransientState<ProgramTreeTransientState> {
private static final String DEFAULT_TREE_NAME = "Program Tree";
private static final String PROGRAM_TREE_OPTION_NAME = "Program Tree";
@@ -197,8 +199,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Tells a plugin that it is no longer needed. The plugin should remove
* itself from anything that it is registered to and release any resources.
* Tells a plugin that it is no longer needed. The plugin should remove itself from anything
* that it is registered to and release any resources.
*/
@Override
public void dispose() {
@@ -260,8 +262,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Read the data for the plugin upon deserialization; reads what should be
* the current selection in the tree.
* Read the data for the plugin upon deserialization; reads what should be the current selection
* in the tree.
*/
@Override
public void readDataState(SaveState saveState) {
@@ -337,24 +339,23 @@ public class ProgramTreePlugin extends ProgramPlugin
componentProvider.treeViewsRestored(list);
}
record ProgramTreeTransientState(SaveState ss) {}
@Override
public Object getTransientState() {
public ProgramTreeTransientState getTransientState() {
SaveState ss = new SaveState();
writeDataState(ss);
return ss;
return new ProgramTreeTransientState(ss);
}
@Override
public void restoreTransientState(Object state) {
SaveState ss = (SaveState) state;
readDataState(ss);
public void restoreTransientState(ProgramTreeTransientState state) {
readDataState(state.ss);
}
@Override
public void processEvent(PluginEvent event) {
if (event instanceof TreeSelectionPluginEvent) {
TreeSelectionPluginEvent ev = (TreeSelectionPluginEvent) event;
if (event instanceof TreeSelectionPluginEvent ev) {
String treeName = ev.getTreeName();
TreeViewProvider provider = providerMap.get(treeName);
if (provider == null) {
@@ -532,6 +533,7 @@ public class ProgramTreePlugin extends ProgramPlugin
/**
* Method renameView.
*
* @param treeViewProvider the provider
* @param newName the new name
* @return true if renamed
@@ -598,8 +600,7 @@ public class ProgramTreePlugin extends ProgramPlugin
* Get the program tree for the given tree name.
*
* @param treeName name of tree in the program (also the name of the view)
* @return ProgramDnDTree tree, or null if there is no provider for the
* given name
* @return ProgramDnDTree tree, or null if there is no provider for the given name
*/
ProgramDnDTree getTree(String treeName) {
TreeViewProvider provider = providerMap.get(treeName);
@@ -623,8 +624,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Notification from the program domain object change listener that a
* fragment was moved; update all the view maps.
* Notification from the program domain object change listener that a fragment was moved; update
* all the view maps.
*/
void fragmentMoved() {
for (String treeName : providerMap.keySet()) {
@@ -636,8 +637,8 @@ public class ProgramTreePlugin extends ProgramPlugin
/**
* The program was restored from an Undo/Redo operation so reload it
*
* @param checkRoot if true, only rebuild the tree if the root node is invalid; if false,
* force a rebuild of the tree
* @param checkRoot if true, only rebuild the tree if the root node is invalid; if false, force
* a rebuild of the tree
*/
void reloadProgram(boolean checkRoot) {
if (currentProgram == null) {
@@ -676,8 +677,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Remember expansion and selection state, and the reload the program
* because it just got restored from an undo operation.
* Remember expansion and selection state, and the reload the program because it just got
* restored from an undo operation.
*/
private void reloadTree(final ProgramDnDTree tree, boolean checkRoot) {
if (tree == null) {
@@ -769,8 +770,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Return true if a tree with the given name exists in the program. If
* program is null and if the tree name is the default name, return true.
* Return true if a tree with the given name exists in the program. If program is null and if
* the tree name is the default name, return true.
*
* @param treeName tree name to look for
* @return boolean
@@ -795,8 +796,7 @@ public class ProgramTreePlugin extends ProgramPlugin
/**
* Set the program on each of the providers.
*
* @param p program that is being opened; if p is null, then program is
* being closed.
* @param p program that is being opened; if p is null, then program is being closed.
*/
private void setProgram(Program p) {
@@ -831,8 +831,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Add tree views that are in the program; if no trees exist (unlikely),
* then add the default provider.
* Add tree views that are in the program; if no trees exist (unlikely), then add the default
* provider.
*/
private void addTreeViews() {
deregisterService(ViewProviderService.class, defaultProvider);
@@ -863,8 +863,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Callback from the create default view action in the provider. Add a
* one-up number to the default name.
* Callback from the create default view action in the provider. Add a one-up number to the
* default name.
*/
private void createDefaultTreeView() {
Listing listing = currentProgram.getListing();
@@ -891,9 +891,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Method called when the "Open Tree View" icon is hit; creates a window
* relative to the event source (in this case, a button) and shows the list
* of trees currently in the Program.
* Method called when the "Open Tree View" icon is hit; creates a window relative to the event
* source (in this case, a button) and shows the list of trees currently in the Program.
*/
private void openView(Object sourceObject) {
JButton button = sourceObject instanceof JButton ? (JButton) sourceObject : null;
@@ -919,9 +918,8 @@ public class ProgramTreePlugin extends ProgramPlugin
}
/**
* Open an existing view in the program. If a provider already exists for
* the given tree name, make this the current view provider in the view
* manager service.
* Open an existing view in the program. If a provider already exists for the given tree name,
* make this the current view provider in the view manager service.
*
* @param treeName name of tree
*/