From 23a587ca05970770b9465d8c2f4cef43fd2b1213 Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Tue, 16 Aug 2022 11:21:14 -0400
Subject: [PATCH] GP-1984:Add Register and Watch Type Settings
---
.../DebuggerRegistersPlugin.html | 6 +
.../DebuggerWatchesPlugin.html | 6 +
.../core/debug/gui/DebuggerResources.java | 8 ++
.../register/DebuggerRegistersProvider.java | 79 ++++++++++-
.../core/debug/gui/register/RegisterRow.java | 5 +
.../gui/watch/DebuggerWatchesPlugin.java | 31 ++--
.../gui/watch/DebuggerWatchesProvider.java | 132 +++++++++++++++---
.../core/debug/gui/watch/SavedSettings.java | 93 ++++++++++++
.../plugin/core/debug/gui/watch/WatchRow.java | 69 +++++++--
.../DebuggerRegistersProviderTest.java | 40 ++++++
.../watch/DebuggerWatchesProviderTest.java | 114 +++++++++++++++
.../data/DBTraceDataSettingsAdapter.java | 6 -
.../listing/AbstractDBTraceDataComponent.java | 2 +-
.../trace/database/listing/DBTraceData.java | 7 +-
.../listing/UndefinedDBTraceData.java | 3 +-
.../core/data/AbstractSettingsDialog.java | 2 +-
.../plugin/core/data/DataSettingsDialog.java | 2 +-
.../core/data/DataTypeSettingsDialog.java | 2 +-
18 files changed, 540 insertions(+), 67 deletions(-)
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/SavedSettings.java
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html
index 0cc6e5708e..71cb6fa711 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html
+++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html
@@ -92,6 +92,12 @@
recorder, but they can still be populated by the user. Modifying the values of unknown
registers cannot affect the target. Register sets are memorized per compiler specification.
+ Register Type Settings
+
+ This action is available on the context menu when there is a single register selected with a
+ data type assigned. It permits the adjustment of that data type's settings, e.g., to display
+ decimal vs hexadecimal. The settings are saved to the data unit for the register.
+
Enable Edits
This toggle is a write protector for machine state. To modify register values, this toggle
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html
index d3b4cb5546..b35676df36 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html
+++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerWatchesPlugin/DebuggerWatchesPlugin.html
@@ -109,6 +109,12 @@
listing. That is, it attempts to apply the selected data type to the evaluated address, sizing
it to the value's size.
+ Watch Type Settings
+
+ This action is available on the context menu when there is a single watch selected with a
+ data type assigned. It permits the adjustment of that data type's settings, e.g., to display
+ decimal vs. hexadecimal.
+
Select Range
This action is available when there's an active trace, and at least one watch with memory
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
index 1b288160a9..d7488c0222 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/DebuggerResources.java
@@ -2377,4 +2377,12 @@ public interface DebuggerResources {
}
}
+ String NAME_CLEAR_REGISTER_TYPE = "Clear Register Type";
+ String DESCRIPTION_CLEAR_REGISTER_TYPE = "Clear the register's data type";
+
+ String NAME_REGISTER_TYPE_SETTINGS = "Register Type Settings";
+ String DESCRIPTION_REGISTER_TYPE_SETTINGS = "Set the register's data type settings";
+
+ String NAME_WATCH_TYPE_SETTINGS = "Watch Type Settings";
+ String DESCRIPTION_WATCH_TYPE_SETTINGS = "Set the watch's data type settings";
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java
index 5ba13f399b..626ef90f5e 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java
@@ -40,6 +40,7 @@ import docking.actions.PopupActionProvider;
import docking.widgets.table.*;
import docking.widgets.table.ColumnSortState.SortDirection;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
+import ghidra.app.plugin.core.data.DataSettingsDialog;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerProvider;
@@ -53,18 +54,19 @@ import ghidra.base.widgets.table.DataTypeTableCellEditor;
import ghidra.dbg.error.DebuggerModelAccessException;
import ghidra.dbg.target.TargetRegisterBank;
import ghidra.dbg.target.TargetThread;
+import ghidra.docking.settings.Settings;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.options.AutoOptions;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.annotation.*;
-import ghidra.framework.plugintool.AutoService;
-import ghidra.framework.plugintool.ComponentProviderAdapter;
+import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.DataTypeEncodeException;
import ghidra.program.model.lang.*;
+import ghidra.program.model.listing.Data;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.trace.model.*;
import ghidra.trace.model.Trace.*;
@@ -74,8 +76,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.util.*;
-import ghidra.util.Msg;
-import ghidra.util.Swing;
+import ghidra.util.*;
import ghidra.util.data.DataTypeParser.AllowedDataTypes;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.exception.CancelledException;
@@ -87,6 +88,50 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
implements DebuggerProvider, PopupActionProvider {
private static final String KEY_DEBUGGER_COORDINATES = "DebuggerCoordinates";
+ interface ClearRegisterType {
+ String NAME = DebuggerResources.NAME_CLEAR_REGISTER_TYPE;
+ String DESCRIPTION = DebuggerResources.DESCRIPTION_CLEAR_REGISTER_TYPE;
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION);
+ }
+ }
+
+ interface RegisterTypeSettings {
+ String NAME = DebuggerResources.NAME_REGISTER_TYPE_SETTINGS;
+ String DESCRIPTION = DebuggerResources.DESCRIPTION_REGISTER_TYPE_SETTINGS;
+ String HELP_ANCHOR = "type_settings";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .popupMenuPath(NAME)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ /**
+ * This only exists so that tests can access it
+ */
+ protected static class RegisterDataSettingsDialog extends DataSettingsDialog {
+ public RegisterDataSettingsDialog(Data data) {
+ super(data);
+ }
+
+ @Override
+ protected Settings getSettings() {
+ return super.getSettings();
+ }
+
+ @Override
+ protected void okCallback() {
+ super.okCallback();
+ }
+ }
+
protected enum RegisterTableColumns
implements EnumeratedTableColumn {
FAV("Fav", Boolean.class, RegisterRow::isFavorite, RegisterRow::setFavorite, //
@@ -429,7 +474,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
GhidraTable regsTable;
RegistersTableModel regsTableModel = new RegistersTableModel();
- private GhidraTableFilterPanel regsFilterPanel;
+ GhidraTableFilterPanel regsFilterPanel;
Map regMap = new HashMap<>();
private final DebuggerAvailableRegistersDialog availableRegsDialog;
@@ -438,6 +483,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
DockingAction actionCreateSnapshot;
ToggleDockingAction actionEnableEdits;
DockingAction actionClearDataType;
+ DockingAction actionDataTypeSettings;
DebuggerRegisterActionContext myActionContext;
AddressSetView viewKnown;
@@ -618,11 +664,16 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
.onAction(c -> {
})
.buildAndInstallLocal(this);
- actionClearDataType = new ActionBuilder("Clear Register Type", plugin.getName())
+ actionClearDataType = ClearRegisterType.builder(plugin)
.enabledWhen(c -> current.getThread() != null)
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0))
.onAction(c -> clearDataTypeActivated())
.buildAndInstallLocal(this);
+ actionDataTypeSettings = RegisterTypeSettings.builder(plugin)
+ .withContext(DebuggerRegisterActionContext.class)
+ .enabledWhen(this::contextHasSingleRegisterWithType)
+ .onAction(this::dataTypeSettingsActivated)
+ .buildAndInstallLocal(this);
}
private void selectRegistersActivated() {
@@ -653,6 +704,22 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter
row.setDataType(null);
}
+ private boolean contextHasSingleRegisterWithType(DebuggerRegisterActionContext ctx) {
+ return ctx.getSelected() != null && ctx.getSelected().getData() != null;
+ }
+
+ private void dataTypeSettingsActivated(DebuggerRegisterActionContext ctx) {
+ RegisterRow row = ctx.getSelected();
+ if (row == null) {
+ return;
+ }
+ Data data = row.getData();
+ if (data == null) {
+ return;
+ }
+ tool.showDialog(new RegisterDataSettingsDialog(data));
+ }
+
// TODO: "Refresh" action to flush cache and re-fetch selected registers
@Override
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java
index e2ee378a3a..407290fdb4 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java
@@ -21,6 +21,7 @@ import java.util.Objects;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.Language;
import ghidra.program.model.lang.Register;
+import ghidra.program.model.listing.Data;
import ghidra.util.Msg;
public class RegisterRow {
@@ -84,6 +85,10 @@ public class RegisterRow {
return provider.getRegisterValue(register);
}
+ public Data getData() {
+ return provider.getRegisterData(register);
+ }
+
public void setDataType(DataType dataType) {
provider.writeRegisterDataType(register, dataType);
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesPlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesPlugin.java
index 2a3f0fa884..b9ba224867 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesPlugin.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesPlugin.java
@@ -24,24 +24,23 @@ import ghidra.framework.options.SaveState;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
-@PluginInfo( //
- shortDescription = "Debugger watches manager", //
- description = "GUI to watch values of expressions", //
- category = PluginCategoryNames.DEBUGGER, //
- packageName = DebuggerPluginPackage.NAME, //
- status = PluginStatus.RELEASED, //
- eventsConsumed = {
- TraceActivatedPluginEvent.class, //
- }, //
- servicesRequired = { //
- DebuggerModelService.class, //
- DebuggerTraceManagerService.class, //
- DataTypeManagerService.class, // For DataType selection field
- } //
-)
+@PluginInfo(
+ shortDescription = "Debugger watches manager",
+ description = "GUI to watch values of expressions",
+ category = PluginCategoryNames.DEBUGGER,
+ packageName = DebuggerPluginPackage.NAME,
+ status = PluginStatus.RELEASED,
+ eventsConsumed = {
+ TraceActivatedPluginEvent.class,
+ },
+ servicesRequired = {
+ DebuggerModelService.class,
+ DebuggerTraceManagerService.class,
+ DataTypeManagerService.class, // For DataType selection field
+ })
public class DebuggerWatchesPlugin extends AbstractDebuggerPlugin {
- private DebuggerWatchesProvider provider;
+ DebuggerWatchesProvider provider;
public DebuggerWatchesPlugin(PluginTool tool) {
super(tool);
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java
index 27186b9a49..0ac2a1a9a6 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProvider.java
@@ -29,14 +29,18 @@ import javax.swing.*;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
+import org.jdom.Element;
+
import docking.ActionContext;
import docking.WindowPosition;
import docking.action.DockingAction;
import docking.action.ToggleDockingAction;
+import docking.action.builder.ActionBuilder;
import docking.widgets.table.*;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ProgramLocationActionContext;
+import ghidra.app.plugin.core.data.AbstractSettingsDialog;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources;
@@ -47,14 +51,13 @@ import ghidra.app.services.*;
import ghidra.async.AsyncDebouncer;
import ghidra.async.AsyncTimer;
import ghidra.base.widgets.table.DataTypeTableCellEditor;
-import ghidra.docking.settings.Settings;
+import ghidra.docking.settings.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.model.DomainObjectChangeRecord;
import ghidra.framework.options.SaveState;
import ghidra.framework.options.annotation.AutoOptionDefined;
import ghidra.framework.options.annotation.HelpInfo;
-import ghidra.framework.plugintool.AutoService;
-import ghidra.framework.plugintool.ComponentProviderAdapter;
+import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.pcode.exec.trace.TraceSleighUtils;
import ghidra.program.model.address.*;
@@ -71,16 +74,64 @@ import ghidra.trace.model.Trace.TraceMemoryStateChangeType;
import ghidra.trace.model.program.TraceProgramView;
import ghidra.trace.model.time.schedule.TraceSchedule;
import ghidra.trace.util.TraceAddressSpace;
-import ghidra.util.Msg;
-import ghidra.util.Swing;
+import ghidra.util.*;
import ghidra.util.database.UndoableTransaction;
+import ghidra.util.exception.CancelledException;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
import ghidra.util.table.column.AbstractGColumnRenderer;
public class DebuggerWatchesProvider extends ComponentProviderAdapter {
- private static final String KEY_EXPRESSION_LIST = "expressionList";
- private static final String KEY_TYPE_LIST = "typeList";
+ private static final String KEY_ROW_COUNT = "rowCount";
+ private static final String PREFIX_ROW = "row";
+
+ interface WatchTypeSettings {
+ String NAME = DebuggerResources.NAME_WATCH_TYPE_SETTINGS;
+ String DESCRIPTION = DebuggerResources.DESCRIPTION_WATCH_TYPE_SETTINGS;
+ String HELP_ANCHOR = "type_settings";
+
+ static ActionBuilder builder(Plugin owner) {
+ String ownerName = owner.getName();
+ return new ActionBuilder(NAME, ownerName)
+ .description(DESCRIPTION)
+ .popupMenuPath(NAME)
+ .helpLocation(new HelpLocation(ownerName, HELP_ANCHOR));
+ }
+ }
+
+ protected static class WatchDataSettingsDialog extends AbstractSettingsDialog {
+ private final WatchRow row;
+
+ public WatchDataSettingsDialog(WatchRow row) {
+ super("Data Type Settings", row.getDataType().getSettingsDefinitions(),
+ row.getSettings());
+ this.row = row;
+ }
+
+ @Override
+ protected Settings getSettings() {
+ return super.getSettings();
+ }
+
+ @Override
+ protected void okCallback() {
+ super.okCallback();
+ }
+
+ @Override
+ protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
+ if (!settingsDefinition.supportsSuggestedValues()) {
+ return null;
+ }
+ return settingsDefinition.getSuggestedValues(row.getSettings());
+ }
+
+ @Override
+ protected void applySettings() throws CancelledException {
+ copySettings(getSettings(), row.getSettings(), getSettingsDefinitions());
+ row.settingsChanged();
+ }
+ }
protected enum WatchTableColumns implements EnumeratedTableColumn {
EXPRESSION("Expression", String.class, WatchRow::getExpression, WatchRow::setExpression),
@@ -151,6 +202,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
}
}
+ protected static void copySettings(Settings src, Settings dst, SettingsDefinition[] defs) {
+ for (SettingsDefinition sd : defs) {
+ sd.copySetting(src, dst);
+ }
+ }
+
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false;
@@ -299,6 +356,7 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
DockingAction actionSelectAllReads;
DockingAction actionAdd;
DockingAction actionRemove;
+ DockingAction actionDataTypeSettings;
DockingAction actionAddFromLocation;
DockingAction actionAddFromRegister;
@@ -442,6 +500,12 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
.onAction(this::activatedRemove)
.buildAndInstallLocal(this);
+ actionDataTypeSettings = WatchTypeSettings.builder(plugin)
+ .withContext(DebuggerWatchActionContext.class)
+ .enabledWhen(this::selIsOneWithDataType)
+ .onAction(this::activatedDataTypeSettings)
+ .buildAndInstallLocal(this);
+
// Pop-up context actions
actionAddFromLocation = WatchAction.builder(plugin)
.withContext(ProgramLocationActionContext.class)
@@ -491,6 +555,11 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
return false;
}
+ protected boolean selIsOneWithDataType(DebuggerWatchActionContext ctx) {
+ WatchRow row = ctx.getWatchRow();
+ return row != null && row.getDataType() != null;
+ }
+
private void activatedApplyDataType(DebuggerWatchActionContext context) {
if (current.getTrace() == null) {
return;
@@ -526,7 +595,8 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
UndoableTransaction.start(current.getTrace(), "Apply Watch Data Type", true)) {
try {
listing.clearCodeUnits(row.getAddress(), row.getRange().getMaxAddress(), false);
- listing.createData(address, dataType, size);
+ Data data = listing.createData(address, dataType, size);
+ copySettings(row.getSettings(), data, dataType.getSettingsDefinitions());
}
catch (CodeUnitInsertionException e) {
errs.add(address + " " + dataType + "(" + size + "): " + e.getMessage());
@@ -579,6 +649,18 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
watchTableModel.deleteWith(context.getWatchRows()::contains);
}
+ private void activatedDataTypeSettings(DebuggerWatchActionContext context) {
+ WatchRow row = context.getWatchRow();
+ if (row == null) {
+ return;
+ }
+ DataType type = row.getDataType();
+ if (type == null) {
+ return;
+ }
+ tool.showDialog(new WatchDataSettingsDialog(row));
+ }
+
private ProgramLocation getDynamicLocation(ProgramLocation someLoc) {
if (someLoc == null) {
return null;
@@ -797,26 +879,30 @@ public class DebuggerWatchesProvider extends ComponentProviderAdapter {
public void writeConfigState(SaveState saveState) {
List rows = List.copyOf(watchTableModel.getModelData());
- String[] expressions = rows.stream().map(WatchRow::getExpression).toArray(String[]::new);
- String[] types = rows.stream().map(WatchRow::getTypePath).toArray(String[]::new);
- saveState.putStrings(KEY_EXPRESSION_LIST, expressions);
- saveState.putStrings(KEY_TYPE_LIST, types);
+ saveState.putInt(KEY_ROW_COUNT, rows.size());
+ for (int i = 0; i < rows.size(); i++) {
+ WatchRow row = rows.get(i);
+ String stateName = PREFIX_ROW + i;
+ SaveState rowState = new SaveState();
+ row.writeConfigState(rowState);
+ saveState.putXmlElement(stateName, rowState.saveToXml());
+ }
}
public void readConfigState(SaveState saveState) {
- String[] expressions = saveState.getStrings(KEY_EXPRESSION_LIST, new String[] {});
- String[] types = saveState.getStrings(KEY_TYPE_LIST, new String[] {});
- if (expressions.length != types.length) {
- Msg.error(this, "Watch provider config error. Unequal number of expressions and types");
- return;
- }
- int len = expressions.length;
+ int rowCount = saveState.getInt(KEY_ROW_COUNT, 0);
List rows = new ArrayList<>();
- for (int i = 0; i < len; i++) {
- WatchRow r = new WatchRow(this, expressions[i]);
- r.setTypePath(types[i]);
- rows.add(r);
+ for (int i = 0; i < rowCount; i++) {
+ String stateName = PREFIX_ROW + i;
+ Element rowElement = saveState.getXmlElement(stateName);
+ if (rowElement != null) {
+ WatchRow r = new WatchRow(this, "");
+ SaveState rowState = new SaveState(rowElement);
+ r.readConfigState(rowState);
+ rows.add(r);
+ }
}
+ watchTableModel.clear();
watchTableModel.addAll(rows);
}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/SavedSettings.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/SavedSettings.java
new file mode 100644
index 0000000000..ef2ca83e60
--- /dev/null
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/SavedSettings.java
@@ -0,0 +1,93 @@
+/* ###
+ * 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.debug.gui.watch;
+
+import ghidra.docking.settings.*;
+import ghidra.framework.options.SaveState;
+import ghidra.program.model.data.TypeDefSettingsDefinition;
+
+public class SavedSettings {
+ private final Settings settings;
+ private SaveState state;
+
+ public SavedSettings(Settings settings) {
+ this.settings = settings;
+ setState(state);
+ }
+
+ public void setState(SaveState state) {
+ if (state == null) {
+ state = new SaveState("Settings");
+ }
+ this.state = state;
+ }
+
+ public SaveState getState() {
+ return state;
+ }
+
+ public void write(SettingsDefinition[] definitions, Settings defaultSettings) {
+ for (SettingsDefinition sd : definitions) {
+ if (sd.hasSameValue(settings, defaultSettings)) {
+ continue;
+ }
+ if (sd instanceof BooleanSettingsDefinition bsd) {
+ state.putBoolean(sd.getStorageKey(), bsd.getValue(settings));
+ }
+ else if (sd instanceof EnumSettingsDefinition esd) {
+ state.putInt(sd.getStorageKey(), esd.getChoice(settings));
+ }
+ else if (sd instanceof NumberSettingsDefinition nsd) {
+ state.putLong(sd.getStorageKey(), nsd.getValue(settings));
+ }
+ else if (sd instanceof StringSettingsDefinition ssd) {
+ state.putString(sd.getStorageKey(), ssd.getValue(settings));
+ }
+ else if (sd instanceof TypeDefSettingsDefinition tdsd) {
+ // Toss this on the floor
+ }
+ else {
+ throw new AssertionError();
+ }
+ }
+ }
+
+ public void read(SettingsDefinition[] definitions, Settings defaultSettings) {
+ for (SettingsDefinition sd : definitions) {
+ if (!state.hasValue(sd.getStorageKey())) {
+ continue;
+ }
+ if (sd instanceof BooleanSettingsDefinition bsd) {
+ bsd.setValue(settings, state.getBoolean(sd.getStorageKey(), false));
+ }
+ else if (sd instanceof EnumSettingsDefinition esd) {
+ esd.setChoice(settings, state.getInt(sd.getStorageKey(), 0));
+ }
+ else if (sd instanceof NumberSettingsDefinition nsd) {
+ nsd.setValue(settings, state.getLong(sd.getStorageKey(), 0));
+ }
+ else if (sd instanceof StringSettingsDefinition ssd) {
+ ssd.setValue(settings, state.getString(sd.getStorageKey(), null));
+ }
+ else if (sd instanceof TypeDefSettingsDefinition tdsd) {
+ // Toss this on the floor
+ }
+ else {
+ throw new AssertionError();
+ }
+ }
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
index bbe47dd173..3ad1c468a4 100644
--- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
+++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/watch/WatchRow.java
@@ -27,7 +27,9 @@ import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.services.DebuggerStateEditingService;
import ghidra.app.services.DebuggerStateEditingService.StateEditor;
+import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsImpl;
+import ghidra.framework.options.SaveState;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.trace.TraceBytesPcodeExecutorState;
import ghidra.pcode.exec.trace.TraceSleighUtils;
@@ -51,6 +53,9 @@ import ghidra.util.*;
public class WatchRow {
public static final int TRUNCATE_BYTES_LENGTH = 64;
+ private static final String KEY_EXPRESSION = "expression";
+ private static final String KEY_DATA_TYPE = "dataType";
+ private static final String KEY_SETTINGS = "settings";
private final DebuggerWatchesProvider provider;
private Trace trace;
@@ -63,6 +68,8 @@ public class WatchRow {
private String expression;
private String typePath;
private DataType dataType;
+ private SettingsImpl settings = new SettingsImpl();
+ private SavedSettings savedSettings = new SavedSettings(settings);
private PcodeExpression compiled;
private TraceMemoryState state;
@@ -150,7 +157,7 @@ public class WatchRow {
return "";
}
MemBuffer buffer = new ByteMemBufferImpl(address, value, language.isBigEndian());
- return dataType.getRepresentation(buffer, SettingsImpl.NO_SETTINGS, value.length);
+ return dataType.getRepresentation(buffer, settings, value.length);
}
// TODO: DataType settings
@@ -296,18 +303,23 @@ public class WatchRow {
protected void updateType() {
dataType = null;
- if (trace == null || typePath == null) {
+ if (typePath == null) {
return;
}
- dataType = trace.getDataTypeManager().getDataType(typePath);
- if (dataType != null) {
- return;
+ // Try from the trace first
+ if (trace != null) {
+ dataType = trace.getDataTypeManager().getDataType(typePath);
+ if (dataType != null) {
+ return;
+ }
}
+ // Either we have no trace, or the trace doesn't have the type.
+ // Try built-ins
DataTypeManagerService dtms = provider.getTool().getService(DataTypeManagerService.class);
- if (dtms == null) {
- return;
+ if (dtms != null) {
+ dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath);
}
- dataType = dtms.getBuiltInDataTypesManager().getDataType(typePath);
+ // We're out of things to try, let null be null
}
public void setTypePath(String typePath) {
@@ -325,12 +337,37 @@ public class WatchRow {
valueString = parseAsDataTypeStr();
valueObj = parseAsDataTypeObj();
provider.contextChanged();
+ settings.setDefaultSettings(dataType == null ? null : dataType.getDefaultSettings());
+ if (dataType != null) {
+ savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
+ }
}
public DataType getDataType() {
return dataType;
}
+ /**
+ * Get the row's (mutable) data type settings
+ *
+ *
+ * After mutating these settings, the client must call {@link #settingsChanged()} to update the
+ * row's display and save state.
+ *
+ * @return the settings
+ */
+ public Settings getSettings() {
+ return settings;
+ }
+
+ public void settingsChanged() {
+ if (dataType != null) {
+ savedSettings.write(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
+ }
+ valueString = parseAsDataTypeStr();
+ provider.watchTableModel.fireTableDataChanged();
+ }
+
public Address getAddress() {
return address;
}
@@ -552,4 +589,20 @@ public class WatchRow {
}
return !Arrays.equals(value, prevValue);
}
+
+ protected void writeConfigState(SaveState saveState) {
+ saveState.putString(KEY_EXPRESSION, expression);
+ saveState.putString(KEY_DATA_TYPE, typePath);
+ saveState.putSaveState(KEY_SETTINGS, savedSettings.getState());
+ }
+
+ protected void readConfigState(SaveState saveState) {
+ setExpression(saveState.getString(KEY_EXPRESSION, ""));
+ setTypePath(saveState.getString(KEY_DATA_TYPE, null));
+
+ savedSettings.setState(saveState.getSaveState(KEY_SETTINGS));
+ if (dataType != null) {
+ savedSettings.read(dataType.getSettingsDefinitions(), dataType.getDefaultSettings());
+ }
+ }
}
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java
index 54e24987ac..1bddd5c7a9 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProviderTest.java
@@ -33,11 +33,14 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.action.LocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.action.NoneLocationTrackingSpec;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
+import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterDataSettingsDialog;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider.RegisterTableColumns;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
import ghidra.app.services.DebuggerStateEditingService;
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
import ghidra.app.services.TraceRecorder;
+import ghidra.docking.settings.FormatSettingsDefinition;
+import ghidra.docking.settings.Settings;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.util.CodeUnitInsertionException;
@@ -460,6 +463,43 @@ public class DebuggerRegistersProviderTest extends AbstractGhidraHeadedDebuggerG
assertTypeEquals(PointerDataType.dataType, data.getDataType());
}
+ @Test
+ public void testModifyTypeSettingsAffectsTrace() throws Exception {
+ traceManager.openTrace(tb.trace);
+
+ TraceThread thread = addThread();
+ try (UndoableTransaction tid = tb.startTransaction()) {
+ tb.exec(0, 0, thread, List.of("pc = 100;"));
+ }
+ traceManager.activateThread(thread);
+ waitForSwing();
+
+ RegisterRow row = findRegisterRow(pc);
+ row.setDataType(LongLongDataType.dataType);
+ waitForSwing();
+
+ DBTraceCodeRegisterSpace regCode =
+ tb.trace.getCodeManager().getCodeRegisterSpace(thread, false);
+ assertNotNull(regCode);
+ TraceData data = regCode.data().getForRegister(0L, pc);
+ assertTypeEquals(LongLongDataType.dataType, data.getDataType());
+ assertEquals("64h", row.getRepresentation());
+
+ registersProvider.regsFilterPanel.setSelectedItem(row);
+ waitForSwing();
+ performEnabledAction(registersProvider, registersProvider.actionDataTypeSettings, false);
+ RegisterDataSettingsDialog dialog =
+ waitForDialogComponent(RegisterDataSettingsDialog.class);
+ Settings settings = dialog.getSettings();
+ FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
+ format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
+ runSwing(() -> dialog.okCallback());
+
+ // The data is the settings. Wonderful :/
+ assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(data));
+ assertEquals("100", row.getRepresentation());
+ }
+
@Test
public void testModifySubRegTypeAffectsTrace() throws Exception {
traceManager.openTrace(tb.trace);
diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java
index afaa2067ed..8c96a006f2 100644
--- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java
+++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/watch/DebuggerWatchesProviderTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.junit.*;
@@ -33,15 +34,20 @@ import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingPlugin;
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
import ghidra.app.plugin.core.debug.gui.register.*;
+import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider.WatchDataSettingsDialog;
import ghidra.app.plugin.core.debug.service.editing.DebuggerStateEditingServicePlugin;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.services.*;
import ghidra.app.services.DebuggerStateEditingService.StateEditingMode;
import ghidra.dbg.model.TestTargetRegisterBankInThread;
+import ghidra.docking.settings.FormatSettingsDefinition;
+import ghidra.docking.settings.Settings;
+import ghidra.framework.options.SaveState;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
+import ghidra.program.model.listing.Data;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
@@ -173,6 +179,76 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(TraceRegisterUtils.rangeForRegister(r0), row.getRange());
}
+ @Test
+ public void testActionApplyDataType() {
+ setRegisterValues(thread);
+ WatchRow row = watchesProvider.addWatch("*:4 r0");
+ row.setDataType(LongDataType.dataType);
+ FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
+ format.setChoice(row.getSettings(), FormatSettingsDefinition.DECIMAL);
+
+ traceManager.openTrace(tb.trace);
+ traceManager.activateThread(thread);
+ watchesProvider.watchFilterPanel.setSelectedItem(row);
+ waitForSwing();
+
+ performEnabledAction(watchesProvider, watchesProvider.actionApplyDataType, true);
+
+ Data u400000 = tb.trace.getCodeManager().data().getAt(0, tb.addr(0x00400000));
+ assertTrue(LongDataType.dataType.isEquivalent(u400000.getDataType()));
+ assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(u400000));
+ }
+
+ @Test
+ public void testWatchWithDataTypeSettings() {
+ setRegisterValues(thread);
+
+ performAction(watchesProvider.actionAdd);
+ WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
+ row.setExpression("r0");
+ row.setDataType(LongLongDataType.dataType);
+
+ traceManager.openTrace(tb.trace);
+ traceManager.activateThread(thread);
+ waitForSwing();
+
+ assertEquals("0x400000", row.getRawValueString());
+ assertEquals("400000h", row.getValueString());
+ assertNoErr(row);
+
+ Settings settings = row.getSettings();
+ FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
+ runSwing(() -> format.setChoice(settings, FormatSettingsDefinition.DECIMAL));
+ assertEquals("4194304", row.getValueString());
+ }
+
+ @Test
+ public void testActionDataTypeSettings() {
+ setRegisterValues(thread);
+
+ performAction(watchesProvider.actionAdd);
+ WatchRow row = Unique.assertOne(watchesProvider.watchTableModel.getModelData());
+ row.setExpression("r0");
+ row.setDataType(LongLongDataType.dataType);
+
+ traceManager.openTrace(tb.trace);
+ traceManager.activateThread(thread);
+ waitForSwing();
+
+ watchesProvider.watchFilterPanel.setSelectedItem(row);
+ waitForSwing();
+
+ performEnabledAction(watchesProvider, watchesProvider.actionDataTypeSettings, false);
+ WatchDataSettingsDialog dialog = waitForDialogComponent(WatchDataSettingsDialog.class);
+
+ Settings settings = dialog.getSettings();
+ FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
+ format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
+ runSwing(() -> dialog.okCallback());
+
+ assertEquals("4194304", row.getValueString());
+ }
+
@Test
public void testConstantWatch() {
setRegisterValues(thread);
@@ -677,4 +753,42 @@ public class DebuggerWatchesProviderTest extends AbstractGhidraHeadedDebuggerGUI
assertEquals(symbol, row.getSymbol());
}
+
+ @Test
+ public void testSaveConfigState() throws Throwable {
+ // Setup some state
+ WatchRow row0 = watchesProvider.addWatch("r0");
+ WatchRow row1 = watchesProvider.addWatch("*:4 r1");
+
+ row0.setDataType(LongLongDataType.dataType);
+ Settings settings = row0.getSettings();
+ FormatSettingsDefinition format = FormatSettingsDefinition.DEF;
+ format.setChoice(settings, FormatSettingsDefinition.DECIMAL);
+ row0.settingsChanged();
+
+ // Save the state
+ SaveState saveState = new SaveState();
+ watchesPlugin.writeConfigState(saveState);
+
+ // Change some things
+ row1.setDataType(Pointer64DataType.dataType);
+ WatchRow row2 = watchesProvider.addWatch("r2");
+ waitForSwing();
+ assertEquals(Set.of(row0, row1, row2),
+ Set.copyOf(watchesProvider.watchTableModel.getModelData()));
+
+ // Restore saved state
+ watchesPlugin.readConfigState(saveState);
+ waitForSwing();
+
+ // Assert the older state
+ Map rows = watchesProvider.watchTableModel.getModelData()
+ .stream()
+ .collect(Collectors.toMap(r -> r.getExpression(), r -> r));
+ assertEquals(2, rows.size());
+
+ WatchRow rRow0 = rows.get("r0");
+ assertTrue(LongLongDataType.dataType.isEquivalent(rRow0.getDataType()));
+ assertEquals(FormatSettingsDefinition.DECIMAL, format.getChoice(rRow0.getSettings()));
+ }
}
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsAdapter.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsAdapter.java
index 21353d9a82..43fe788b75 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsAdapter.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/data/DBTraceDataSettingsAdapter.java
@@ -31,7 +31,6 @@ import ghidra.trace.database.map.*;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree.AbstractDBTraceAddressSnapRangePropertyMapData;
import ghidra.trace.database.thread.DBTraceThreadManager;
import ghidra.trace.model.thread.TraceThread;
-import ghidra.trace.util.TraceAddressSpace;
import ghidra.util.database.*;
import ghidra.util.database.annot.*;
import ghidra.util.exception.VersionException;
@@ -224,11 +223,6 @@ public class DBTraceDataSettingsAdapter
dataFactory);
}
- @Override
- public DBTraceDataSettingsSpace get(TraceAddressSpace space, boolean createIfAbsent) {
- return (DBTraceDataSettingsSpace) super.get(space, createIfAbsent);
- }
-
@Override
public DBTraceDataSettingsSpace getForSpace(AddressSpace space, boolean createIfAbsent) {
return (DBTraceDataSettingsSpace) super.getForSpace(space, createIfAbsent);
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java
index 9a6d5bffc7..dc447229e6 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/AbstractDBTraceDataComponent.java
@@ -231,7 +231,7 @@ public abstract class AbstractDBTraceDataComponent implements DBTraceDefinedData
@Override
public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
- return root.getSettingsSpace(createIfAbsent);
+ return (DBTraceDataSettingsSpace) root.getSettingsSpace(createIfAbsent);
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java
index 98fe97f0aa..3e22af51fc 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/DBTraceData.java
@@ -25,7 +25,7 @@ import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Language;
import ghidra.trace.database.DBTraceUtils;
-import ghidra.trace.database.data.DBTraceDataSettingsAdapter.DBTraceDataSettingsSpace;
+import ghidra.trace.database.data.DBTraceDataSettingsOperations;
import ghidra.trace.database.guest.InternalTracePlatform;
import ghidra.trace.database.map.DBTraceAddressSnapRangePropertyMapTree;
import ghidra.trace.model.guest.TracePlatform;
@@ -243,8 +243,9 @@ public class DBTraceData extends AbstractDBTraceCodeUnit
}
@Override
- public DBTraceDataSettingsSpace getSettingsSpace(boolean createIfAbsent) {
- return getTrace().getDataSettingsAdapter().get(space, createIfAbsent);
+ public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
+ return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
+ .get(space, createIfAbsent);
}
@Override
diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java
index d55a17b78a..aa8c7a97ff 100644
--- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java
+++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/database/listing/UndefinedDBTraceData.java
@@ -276,7 +276,8 @@ public class UndefinedDBTraceData implements DBTraceDataAdapter, DBTraceSpaceKey
@Override
public DBTraceDataSettingsOperations getSettingsSpace(boolean createIfAbsent) {
- return getTrace().getDataSettingsAdapter().get(this, createIfAbsent);
+ return (DBTraceDataSettingsOperations) getTrace().getDataSettingsAdapter()
+ .get(this, createIfAbsent);
}
@Override
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java
index a7102eea02..b261da7826 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/AbstractSettingsDialog.java
@@ -258,7 +258,7 @@ public abstract class AbstractSettingsDialog extends DialogComponentProvider {
* @param settingsDefinition string settings definition
* @return suggested string value (may be empty array or null)
*/
- abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition);
+ protected abstract String[] getSuggestedValues(StringSettingsDefinition settingsDefinition);
/**
* Apply changes to settings. This method must be ov
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataSettingsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataSettingsDialog.java
index 5ebd839a1d..41ba8db891 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataSettingsDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataSettingsDialog.java
@@ -336,7 +336,7 @@ public class DataSettingsDialog extends AbstractSettingsDialog {
}
@Override
- String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
+ protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (!settingsDefinition.supportsSuggestedValues()) {
return null;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java
index 147a4edc84..75f11419fe 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/data/DataTypeSettingsDialog.java
@@ -140,7 +140,7 @@ public class DataTypeSettingsDialog extends AbstractSettingsDialog {
}
@Override
- String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
+ protected String[] getSuggestedValues(StringSettingsDefinition settingsDefinition) {
if (settingsDefinition.supportsSuggestedValues()) {
return settingsDefinition.getSuggestedValues(getOriginalSettings());
}