diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisOptionsUpdater.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisOptionsUpdater.java
new file mode 100644
index 0000000000..0f8f5e1387
--- /dev/null
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisOptionsUpdater.java
@@ -0,0 +1,127 @@
+/* ###
+ * 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.analysis;
+
+import java.util.*;
+import java.util.function.Function;
+
+import ghidra.framework.options.Options;
+
+/**
+ * An object that allows analyzers to rename options. This is required to move old options stored
+ * in the program to the new equivalent option. This class is not required for options that have
+ * simply been removed.
+ *
+ * Notes:
+ *
+ *
+ * Replacement options must be registered with one of the register methods of this class.
+ *
+ *
+ * This is intended for use with the UI; access analysis options from the API will not use this
+ * replacer. This means that any client, such as script, retrieving the old option value will not
+ * work for new programs that no longer have that old option registered. Further, for programs
+ * that have the old options saved, but no longer registered, changing the old option value will
+ * have no effect.
+ *
+ *
+ * Old option values will only be used if they are non-default and the new option value is default.
+ *
+ *
+ * Clients can change the type of the option if they wish using
+ * {@link #registerReplacement(String, String, Function)}.
+ *
+ *
+ */
+public class AnalysisOptionsUpdater {
+
+ private static final Function OLD_VALUE_REPLACER = oldValue -> oldValue;
+
+ private Map optionsByNewName = new HashMap<>();
+
+ /**
+ * Register the given old option name to be replaced with the new option name. The
+ * replacement strategy used in this case will be to return the old value for the new option.
+ * @param newOptionName the new option name
+ * @param oldOptionName the old option name
+ */
+ public void registerReplacement(String newOptionName, String oldOptionName) {
+
+ registerReplacement(newOptionName, oldOptionName, OLD_VALUE_REPLACER);
+ }
+
+ /**
+ * Register the given old option name to be replaced with the new option name. The given
+ * replacer function will be called with the old option value to get the new option value.
+ * @param newOptionName the new option name
+ * @param oldOptionName the old option name
+ * @param replacer the function to update the update the old option value
+ */
+ public void registerReplacement(String newOptionName, String oldOptionName,
+ Function replacer) {
+
+ optionsByNewName.put(newOptionName,
+ new ReplaceableOption(newOptionName, oldOptionName, replacer));
+ }
+
+ Set getReplaceableOptions() {
+ return new HashSet<>(optionsByNewName.values());
+ }
+
+ /**
+ * A simple object that contains the new and old option name along with the replacer function
+ * that will handle the option replacement.
+ */
+ public static class ReplaceableOption {
+
+ private final String newName;
+ private final String oldName;
+ private final Function replacer;
+
+ ReplaceableOption(String newName, String oldName, Function replacer) {
+ this.newName = newName;
+ this.oldName = oldName;
+ this.replacer = replacer;
+ }
+
+ // note: this method expects to be called within a transaction
+ void replace(Options options) {
+ Object oldValue = options.getObject(oldName, null);
+ if (oldValue == null) {
+ return;
+ }
+
+ if (options.isDefaultValue(oldName)) {
+ return;
+ }
+
+ if (!options.isDefaultValue(newName)) {
+ return; // don't overwrite user's updated value
+ }
+
+ Object newValue = replacer.apply(oldValue);
+ options.putObject(newName, newValue);
+ }
+
+ String getNewName() {
+ return newName;
+ }
+
+ String getOldName() {
+ return oldName;
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java
index 44a0703718..07723d28c6 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalysisPanel.java
@@ -40,6 +40,7 @@ import docking.widgets.combobox.GhidraComboBox;
import docking.widgets.label.GLabel;
import docking.widgets.table.GTable;
import ghidra.GhidraOptions;
+import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater.ReplaceableOption;
import ghidra.app.services.Analyzer;
import ghidra.framework.Application;
import ghidra.framework.GenericRunInfo;
@@ -70,13 +71,15 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
// preference which retains last used analyzer_options file name
public static final String LAST_USED_OPTIONS_CONFIG = "LAST_USED_OPTIONS_CONFIG";
+ static final String ANALYZER_OPTIONS_PANEL_NAME = "analyzer.options.panel";
+
private List programs;
private PropertyChangeListener propertyChangeListener;
private Options analysisOptions;
private Options currentProgramOptions; // this will have all the non-default options from the program
private Options selectedOptions = STANDARD_DEFAULT_OPTIONS;
- private JTable table;
+ private GTable table;
private AnalysisEnablementTableModel model;
private JTextArea descriptionComponent;
private JPanel analyzerOptionsPanel;
@@ -130,6 +133,9 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
currentProgramOptions = getNonDefaultProgramOptions();
setName("Analysis Panel");
build();
+
+ replaceOldOptions();
+
load();
loadCurrentOptionsIntoEditors();
}
@@ -169,13 +175,13 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
Program program = programs.get(0);
AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
- List propertyNames = analysisOptions.getOptionNames();
- Collections.sort(propertyNames, (o1, o2) -> o1.compareToIgnoreCase(o2));
- for (String analyzerName : propertyNames) {
+ List optionNames = analysisOptions.getOptionNames();
+ Collections.sort(optionNames, (o1, o2) -> o1.compareToIgnoreCase(o2));
+ for (String analyzerName : optionNames) {
if (analyzerName.indexOf('.') == -1) {
if (analysisOptions.getType(analyzerName) != OptionType.BOOLEAN_TYPE) {
throw new AssertException(
- "Analyzer enable property that is not boolean - " + analyzerName);
+ "Analyzer 'enable' property that is not boolean - " + analyzerName);
}
Analyzer analyzer = manager.getAnalyzer(analyzerName);
@@ -408,7 +414,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
private void reloadOptionsCombo(Options newDefaultOptions) {
optionConfigurationChoices = loadPossibleOptionsChoicesForComboBox();
- optionsComboBox.setModel(new DefaultComboBoxModel(optionConfigurationChoices));
+ optionsComboBox.setModel(new DefaultComboBoxModel<>(optionConfigurationChoices));
Options selected = findOptionsByName(newDefaultOptions.getName());
optionsComboBox.setSelectedItem(selected);
}
@@ -587,6 +593,66 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
}
}
+ private void replaceOldOptions() {
+
+ for (Program program : programs) {
+
+ boolean commit = false;
+ int id = program.startTransaction("Replacing old analysis properties");
+ try {
+ doReplaceOldOptions(program);
+ commit = true;
+ }
+ finally {
+ program.endTransaction(id, commit);
+ }
+ }
+ }
+
+ private void doReplaceOldOptions(Program program) {
+
+ AutoAnalysisManager manager = AutoAnalysisManager.getAnalysisManager(program);
+
+ Options programAnalysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ List allAnalyzersOptions = programAnalysisOptions.getChildOptions();
+ for (Options analyzerOptions : allAnalyzersOptions) {
+ String analyzerName = analyzerOptions.getName();
+ Analyzer analyzer = manager.getAnalyzer(analyzerName);
+ if (analyzer == null) {
+ // can be null if an analyzer no longer exists
+ continue;
+ }
+ AnalysisOptionsUpdater updater = analyzer.getOptionsUpdater();
+ if (updater == null) {
+ continue;
+ }
+
+ applyOptionUpdater(analyzerOptions, updater);
+ }
+ }
+
+ private void applyOptionUpdater(Options analyzerOptions, AnalysisOptionsUpdater updater) {
+
+ Set replaceableOptions = updater.getReplaceableOptions();
+ for (ReplaceableOption ro : replaceableOptions) {
+ String newName = ro.getNewName();
+ String oldName = ro.getOldName();
+ if (!analyzerOptions.contains(oldName)) {
+ continue; // the old option was never saved or has been removed
+ }
+
+ if (!analyzerOptions.contains(newName)) {
+ Msg.error(this,
+ "Found an option replacer without having the new option registered" +
+ "new option: '" + newName + "'; old option: '" + oldName + "'");
+ continue;
+ }
+
+ ro.replace(analyzerOptions);
+ analyzerOptions.removeOption(ro.getOldName());
+ }
+ }
+
private void loadAnalyzerOptionsPanels() {
List optionGroups = analysisOptions.getChildOptions();
noOptionsPanel = new JPanel(new VerticalLayout(5));
@@ -599,6 +665,7 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
String analyzerName = optionsGroup.getName();
JPanel optionsContainer = new JPanel(new VerticalLayout(5));
+ optionsContainer.setName(ANALYZER_OPTIONS_PANEL_NAME);
optionsContainer.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
JScrollPane scrollPane = new JScrollPane(optionsContainer);
@@ -645,11 +712,17 @@ class AnalysisPanel extends JPanel implements PropertyChangeListener {
List subOptions = optionsGroup.getLeafOptionNames();
Iterator it = subOptions.iterator();
while (it.hasNext()) {
- String next = it.next();
- if (!isEditable(optionsGroup, next)) {
+ String name = it.next();
+ if (!isEditable(optionsGroup, name)) {
+ it.remove();
+ }
+
+ // also filter out unregistered options
+ if (!optionsGroup.isRegistered(name)) {
it.remove();
}
}
+
return subOptions;
}
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java
index 4cc060f1c9..93cb4dbc1d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AnalyzeAllOpenProgramsTask.java
@@ -90,8 +90,7 @@ class AnalyzeAllOpenProgramsTask extends Task {
return; // no need to log this - it's a valid condition
}
- AutoAnalysisManager mgr = AutoAnalysisManager.getAnalysisManager(prototypeProgram);
- if (!setOptions(prototypeProgram, mgr)) {
+ if (!setOptions(prototypeProgram)) {
return;
}
@@ -169,7 +168,7 @@ class AnalyzeAllOpenProgramsTask extends Task {
return true;
}
- private boolean setOptions(final Program program, AutoAnalysisManager mgr) {
+ private boolean setOptions(final Program program) {
AtomicBoolean analyze = new AtomicBoolean();
int id = program.startTransaction("analysis");
try {
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java
index 802540285e..f7c17f9a1d 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/AutoAnalysisPlugin.java
@@ -63,21 +63,15 @@ import ghidra.util.task.TaskLauncher;
public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerListener {
private static final String SHOW_ANALYSIS_OPTIONS = "Show Analysis Options";
-
private static final String ANALYZE_GROUP_NAME = "Analyze";
private DockingAction autoAnalyzeAction;
- private DockingAction analyzeAllAction;
private HelpLocation helpLocation;
private List analyzers = new ArrayList<>();
private List oneShotActions = new ArrayList<>();
- /**
- * Creates a new instance of the plugin giving it the tool that it will work
- * in.
- */
public AutoAnalysisPlugin(PluginTool tool) {
super(tool);
@@ -125,8 +119,7 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
.onAction(this::analyzeCallback)
.buildAndInstall(tool);
- analyzeAllAction =
- new ActionBuilder("Analyze All Open", getName())
+ new ActionBuilder("Analyze All Open", getName())
.supportsDefaultToolContext(true)
.menuPath("&Analysis", "Analyze All &Open...")
.menuGroup(ANALYZE_GROUP_NAME, "" + subGroupIndex++)
@@ -206,31 +199,18 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
analysisMgr.reAnalyzeAll(selection);
}
- /**
- * Get the description of this plugin.
- */
public static String getDescription() {
return "Provides coordination and a service for All Auto Analysis tasks";
}
- /**
- * Get the descriptive name.
- */
public static String getDescriptiveName() {
return "AutoAnalysisManager";
}
- /**
- * Get the category.
- */
public static String getCategory() {
return "Analysis";
}
- /***************************************************************************
- * Implementation of AutoAnalysis Service
- */
-
protected void programClosed(Program program) {
if (AutoAnalysisManager.hasAutoAnalysisManager(program)) {
@@ -276,9 +256,10 @@ public class AutoAnalysisPlugin extends Plugin implements AutoAnalysisManagerLis
private void programActivated(final Program program) {
- program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST).registerOption(
- StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
- "Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
+ program.getOptions(StoredAnalyzerTimes.OPTIONS_LIST)
+ .registerOption(
+ StoredAnalyzerTimes.OPTION_NAME, OptionType.CUSTOM_TYPE, null, null,
+ "Cumulative analysis task times", new StoredAnalyzerTimesPropertyEditor());
// invokeLater() to ensure that all other plugins have been notified of the program
// activated. This makes sure plugins like the Listing have opened and painted the
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
index 6773377546..12ec378c13 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/DWARFAnalyzer.java
@@ -36,50 +36,79 @@ import ghidra.util.task.TaskMonitor;
public class DWARFAnalyzer extends AbstractAnalyzer {
private static final String DWARF_LOADED_OPTION_NAME = "DWARF Loaded";
- private static final String OPTION_IMPORT_DATATYPES = "Import data types";
+ private static final String OPTION_IMPORT_DATATYPES = "Import Data Types";
private static final String OPTION_IMPORT_DATATYPES_DESC =
"Import data types defined in the DWARF debug info.";
- private static final String OPTION_PRELOAD_ALL_DIES = "Preload all DIEs";
+ private static final String OPTION_PRELOAD_ALL_DIES = "Preload All DIEs";
private static final String OPTION_PRELOAD_ALL_DIES_DESC =
- "Preload all DIE records. Requires more memory, but necessary for some non-standard layouts.";
+ "Preload all DIE records. Requires more memory, but necessary for some non-standard " +
+ "layouts.";
- private static final String OPTION_IMPORT_FUNCS = "Import functions";
+ private static final String OPTION_IMPORT_FUNCS = "Import Functions";
private static final String OPTION_IMPORT_FUNCS_DESC =
- "Import function information defined in the DWARF debug info. (implies import data types)";
+ "Import function information defined in the DWARF debug info\n" +
+ "(implies 'Import Data Types' is selected).";
- private static final String OPTION_IMPORT_LIMIT_DIE_COUNT = "Debug item count limit";
+ private static final String OPTION_IMPORT_LIMIT_DIE_COUNT = "Debug Item Limit";
private static final String OPTION_IMPORT_LIMIT_DIE_COUNT_DESC =
- "If the number of DWARF debug items are greater than this setting, DWARF analysis will be skipped.";
+ "If the number of DWARF debug items are greater than this setting, DWARF analysis will " +
+ "be skipped.";
- private static final String OPTION_OUTPUT_SOURCE_INFO = "Output Source info";
+ private static final String OPTION_OUTPUT_SOURCE_INFO = "Output Source Info";
private static final String OPTION_OUTPUT_SOURCE_INFO_DESC =
- "Include source code location info (filename:linenumber) in comments attached to the Ghidra datatype or function or variable created.";
+ "Include source code location info (filename:linenumber) in comments attached to the " +
+ "Ghidra datatype or function or variable created.";
- private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE info";
+ private static final String OPTION_OUTPUT_DWARF_DIE_INFO = "Output DWARF DIE Info";
private static final String OPTION_OUTPUT_DWARF_DIE_INFO_DESC =
- "Include DWARF DIE offset info in comments attached to the Ghidra datatype or function or variable created.";
+ "Include DWARF DIE offset info in comments attached to the Ghidra datatype or function " +
+ "or variable created.";
- private static final String OPTION_NAME_LENGTH_CUTOFF = "Name length cutoff";
+ private static final String OPTION_NAME_LENGTH_CUTOFF = "Maximum Name Length";
private static final String OPTION_NAME_LENGTH_CUTOFF_DESC =
"Truncate symbol and type names longer than this limit. Range 20..2000";
- private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS = "Lexical block comments";
+ private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS = "Add Lexical Block Comments";
private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_DESC =
"Add comments to the start of lexical blocks";
- private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS = "Inlined functions comments";
+ private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS =
+ "Add Inlined Functions Comments";
private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS_DESC =
"Add comments to the start of inlined functions";
- private static final String OPTION_OUTPUT_FUNC_SIGS = "Output function signatures";
+ private static final String OPTION_OUTPUT_FUNC_SIGS = "Create Function Signatures";
private static final String OPTION_OUTPUT_FUNC_SIGS_DESC =
- "Create function signature data types for each function encountered in the DWARF debug data.";
+ "Create function signature data types for each function encountered in the DWARF debug " +
+ "data.";
private static final String DWARF_ANALYZER_NAME = "DWARF";
private static final String DWARF_ANALYZER_DESCRIPTION =
"Automatically extracts DWARF info from an ELF file.";
+//==================================================================================================
+// Old Option Names - Should stick around for multiple major versions after 10.2
+//==================================================================================================
+
+ private static final String OPTION_IMPORT_DATATYPES_OLD = "Import data types";
+ private static final String OPTION_PRELOAD_ALL_DIES_OLD = "Preload all DIEs";
+ private static final String OPTION_IMPORT_FUNCS_OLD = "Import functions";
+ private static final String OPTION_IMPORT_LIMIT_DIE_COUNT_OLD = "Debug item count limit";
+ private static final String OPTION_OUTPUT_SOURCE_INFO_OLD = "Output Source info";
+ private static final String OPTION_OUTPUT_DWARF_DIE_INFO_OLD = "Output DWARF DIE info";
+ private static final String OPTION_NAME_LENGTH_CUTOFF_OLD = "Name length cutoff";
+ private static final String OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_OLD = "Lexical block comments";
+ private static final String OPTION_OUTPUT_INLINE_FUNC_COMMENTS_OLD =
+ "Inlined functions comments";
+ private static final String OPTION_OUTPUT_FUNC_SIGS_OLD = "Output function signatures";
+
+ private AnalysisOptionsUpdater optionsUpdater = new AnalysisOptionsUpdater();
+
+//==================================================================================================
+// End Old Option Names
+//==================================================================================================
+
/**
* Returns true if DWARF has already been imported into the specified program.
*
@@ -87,8 +116,8 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
* @return true if DWARF has already been imported, false if not yet
*/
public static boolean isAlreadyImported(Program program) {
- Options propList = program.getOptions(Program.PROGRAM_INFO);
- return propList.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
+ Options options = program.getOptions(Program.PROGRAM_INFO);
+ return options.getBoolean(DWARF_LOADED_OPTION_NAME, false) ||
oldCheckIfDWARFImported(program);
}
@@ -100,6 +129,26 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
setDefaultEnablement(true);
setPriority(AnalysisPriority.FORMAT_ANALYSIS.after());
setSupportsOneTimeAnalysis();
+
+ optionsUpdater.registerReplacement(OPTION_IMPORT_DATATYPES,
+ OPTION_IMPORT_DATATYPES_OLD);
+ optionsUpdater.registerReplacement(OPTION_PRELOAD_ALL_DIES,
+ OPTION_PRELOAD_ALL_DIES_OLD);
+ optionsUpdater.registerReplacement(OPTION_IMPORT_FUNCS,
+ OPTION_IMPORT_FUNCS_OLD);
+ optionsUpdater.registerReplacement(OPTION_IMPORT_LIMIT_DIE_COUNT,
+ OPTION_IMPORT_LIMIT_DIE_COUNT_OLD);
+ optionsUpdater.registerReplacement(OPTION_OUTPUT_SOURCE_INFO,
+ OPTION_OUTPUT_SOURCE_INFO_OLD);
+ optionsUpdater.registerReplacement(OPTION_OUTPUT_DWARF_DIE_INFO,
+ OPTION_OUTPUT_DWARF_DIE_INFO_OLD);
+ optionsUpdater.registerReplacement(OPTION_NAME_LENGTH_CUTOFF,
+ OPTION_NAME_LENGTH_CUTOFF_OLD);
+ optionsUpdater.registerReplacement(OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS,
+ OPTION_OUTPUT_LEXICAL_BLOCK_COMMENTS_OLD);
+ optionsUpdater.registerReplacement(OPTION_OUTPUT_INLINE_FUNC_COMMENTS,
+ OPTION_OUTPUT_INLINE_FUNC_COMMENTS_OLD);
+ optionsUpdater.registerReplacement(OPTION_OUTPUT_FUNC_SIGS, OPTION_OUTPUT_FUNC_SIGS_OLD);
}
@Override
@@ -185,6 +234,7 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
@Override
public void registerOptions(Options options, Program program) {
+
options.registerOption(OPTION_IMPORT_DATATYPES, importOptions.isImportDataTypes(), null,
OPTION_IMPORT_DATATYPES_DESC);
@@ -218,6 +268,11 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
null, OPTION_OUTPUT_FUNC_SIGS_DESC);
}
+ @Override
+ public AnalysisOptionsUpdater getOptionsUpdater() {
+ return optionsUpdater;
+ }
+
@Override
public void optionsChanged(Options options, Program program) {
importOptions.setOutputDIEInfo(
diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/Analyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Analyzer.java
index a47e5fbd83..cc5b35fd26 100644
--- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/Analyzer.java
+++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/Analyzer.java
@@ -15,6 +15,7 @@
*/
package ghidra.app.services;
+import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater;
import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView;
@@ -24,12 +25,13 @@ import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
- * NOTE: ALL ANALYZER CLASSES MUST END IN "Analyzer". If not,
- * the ClassSearcher will not find them.
- *
* Interface to perform automatic analysis.
+ *
+ * NOTE: ALL ANALYZER CLASSES MUST END IN "Analyzer". If not, the ClassSearcher will not find
+ * them.
*/
public interface Analyzer extends ExtensionPoint {
+
/**
* Get the name of this analyzer
* @return analyzer name
@@ -43,15 +45,18 @@ public interface Analyzer extends ExtensionPoint {
public AnalyzerType getAnalysisType();
/**
- * Returns true if this analyzer should be enabled by default. Generally useful
- * analyzers should return true. Specialized analyzers should return false;
+ * Returns true if this analyzer should be enabled by default. Generally useful analyzers
+ * should return true. Specialized analyzers should return false;
+ * @param program the program
+ * @return true if enabled by default
*/
public boolean getDefaultEnablement(Program program);
/**
* Returns true if it makes sense for this analyzer to directly invoked on an address or
- * addressSet. The AutoAnalyzer plug-in will automatically create an action for each
- * analyzer that returns true.
+ * addressSet. The AutoAnalyzer plug-in will automatically create an action for each analyzer
+ * that returns true.
+ * @return true if supports one-time analysis
*/
public boolean supportsOneTimeAnalysis();
@@ -75,29 +80,31 @@ public interface Analyzer extends ExtensionPoint {
public boolean canAnalyze(Program program);
/**
- * Called when the requested information type has been added.
- * (ie: function added.)
+ * Called when the requested information type has been added, for example, when a function is
+ * added.
*
* @param program program to analyze
* @param set AddressSet of locations that have been added
- * @param monitor monitor that indicates progress and indicates whether
- * the user canceled the analysis
+ * @param monitor monitor that indicates progress and indicates whether the user canceled the
+ * analysis
* @param log a message log to record analysis information
* @return true if the analysis succeeded
+ * @throws CancelledException if the analysis is cancelled
*/
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException;
/**
- * Called when the requested information type has been removed.
- * (ie: function removed.)
+ * Called when the requested information type has been removed, for example, when a function is
+ * removed.
*
* @param program program to analyze
* @param set AddressSet of locations that have been added
- * @param monitor monitor that indicates progress and indicates whether
- * the user canceled the analysis
+ * @param monitor monitor that indicates progress and indicates whether the user canceled the
+ * analysis
* @param log a message log to record analysis information
* @return true if the analysis succeeded
+ * @throws CancelledException if the analysis is cancelled
*/
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException;
@@ -111,8 +118,18 @@ public interface Analyzer extends ExtensionPoint {
public void registerOptions(Options options, Program program);
/**
- * Analyzers should initialize their options from the values in the given Options,
- * providing appropriate default values.
+ * Returns an optional options updater that allows clients to migrate old options to new
+ * options. This can be used to facilitate option name changes, as well as option value type
+ * changes.
+ * @return the updater; null if no updater
+ */
+ public default AnalysisOptionsUpdater getOptionsUpdater() {
+ return null; // stub; clients will override as needed
+ }
+
+ /**
+ * Analyzers should initialize their options from the values in the given Options, providing
+ * appropriate default values.
* @param options the program options/property list that contains the options
* @param program program to be analyzed
*/
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptions2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptions2Test.java
new file mode 100644
index 0000000000..d4efc22138
--- /dev/null
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptions2Test.java
@@ -0,0 +1,520 @@
+/* ###
+ * 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.analysis;
+
+import static org.junit.Assert.*;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JPanel;
+import javax.swing.table.TableModel;
+
+import org.junit.*;
+
+import docking.ActionContext;
+import docking.action.DockingActionIf;
+import docking.options.editor.DefaultOptionComponent;
+import docking.widgets.table.GTable;
+import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
+import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
+import ghidra.app.services.*;
+import ghidra.app.util.importer.MessageLog;
+import ghidra.framework.options.*;
+import ghidra.framework.plugintool.PluginTool;
+import ghidra.program.database.ProgramBuilder;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.test.AbstractGhidraHeadedIntegrationTest;
+import ghidra.test.TestEnv;
+import ghidra.util.classfinder.ClassSearcher;
+import ghidra.util.exception.AssertException;
+import ghidra.util.exception.CancelledException;
+import ghidra.util.task.TaskMonitor;
+
+/**
+ * This class test the ability to replace analysis options using an {@link AnalysisOptionsUpdater}.
+ */
+public class AnalysisOptions2Test extends AbstractGhidraHeadedIntegrationTest {
+
+ private static final String UNCHANGING_OPTION_NAME = "Unchanging Name";
+ private static final String NEW_OPTION_NAME = "New Option";
+ private static final String OLD_OPTION_NAME = "Old Option";
+ private static final String NEW_OPTION_DEFAULT_VALUE = "New Default Value";
+ private static final String OLD_OPTION_DEFAULT_VALUE = "Old Default Value";
+ private static final String UNCHANGING_OPTION_DEFAULT_VALUE = "Unchanging Default Value";
+
+ private static final Color NEW_OPTION_DEFAULT_VALUE_AS_COLOR = Color.GREEN;
+
+ private TestEnv env;
+ private PluginTool tool;
+ private Program program;
+ private AnalysisOptionsDialog optionsDialog;
+
+ @Before
+ public void setUp() throws Exception {
+
+ env = new TestEnv();
+ tool = env.getTool();
+ tool.addPlugin(CodeBrowserPlugin.class.getName());
+ addPlugin(tool, AutoAnalysisPlugin.class);
+ showTool(tool);
+
+ program = buildProgram("test", ProgramBuilder._TOY);
+ }
+
+ private Program buildProgram(String name, String languageID) throws Exception {
+ ProgramBuilder builder = new ProgramBuilder(name, languageID);
+ builder.createMemory("test1", "0x1000", 0x2000);
+ return builder.getProgram();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ close(optionsDialog);
+ env.dispose();
+ }
+
+ @Test
+ public void testOptionReplacing_DefaultNewValue_DefaultOldValue() throws Exception {
+
+ //
+ // The old option's default value should not be applied to the new option
+ //
+
+ installAnalyzer(NotReplacingTestAnalyzerStub.class);
+
+ // install old options; the default value will not be used in the new option
+ installOldOptions(OLD_OPTION_DEFAULT_VALUE);
+
+ openProgram();
+
+ optionsDialog = invokeAnalysisDialog();
+
+ // check options displayed in the dialog
+ assertOnlyNewOptionsInUi();
+ assertOptionValue(NEW_OPTION_NAME, NEW_OPTION_DEFAULT_VALUE);
+ assertOptionValue(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE);
+ assertOldValueRemoved();
+ }
+
+ @Test
+ public void testOptionReplacing_NonDefaultNewValue_DefaultOldValue() throws Exception {
+
+ //
+ // The old option's default value should not be applied to the new option
+ //
+
+ installAnalyzer(NotReplacingTestAnalyzerStub.class);
+
+ // install old options; the default value will not be used in the new option
+ installOldOptions(OLD_OPTION_DEFAULT_VALUE);
+
+ // put new options in the analyzer's options
+ String newValue = "Some New Value";
+ changeNewOption(newValue);
+
+ openProgram();
+
+ optionsDialog = invokeAnalysisDialog();
+
+ // check options displayed in the dialog
+ assertOnlyNewOptionsInUi();
+ assertOptionValue(NEW_OPTION_NAME, newValue);
+ assertOptionValue(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE);
+ assertOldValueRemoved();
+ }
+
+ @Test
+ public void testOptionReplacing_DefaultNewValue_NonDefaultOldValue() throws Exception {
+
+ //
+ // The old option's non-default value should be applied to the new option
+ //
+
+ installAnalyzer(UseOldValueTestAnalyzerStub.class);
+
+ // install old options; the default value will not be used in the new option
+ installOldOptions(OLD_OPTION_DEFAULT_VALUE);
+ String newValue = "Old Option Non-default Value";
+ changeOldOption(newValue);
+
+ openProgram();
+
+ optionsDialog = invokeAnalysisDialog();
+
+ // check options displayed in the dialog
+ assertOnlyNewOptionsInUi();
+ assertOptionValue(NEW_OPTION_NAME, newValue);
+ assertOptionValue(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE);
+ assertOldValueRemoved();
+ }
+
+ @Test
+ public void testOptionReplacing_NonDefaultNewValue_NonDefaultOldValue() throws Exception {
+
+ //
+ // The old option's non-default value should not be applied to the new option, since the
+ // new option has a non-default
+ //
+
+ installAnalyzer(UseOldValueTestAnalyzerStub.class);
+
+ // install old options; the default value will not be used in the new option
+ installOldOptions(OLD_OPTION_DEFAULT_VALUE);
+ String oldOptionNewValue = "Old Option Non-default Value";
+ changeOldOption(oldOptionNewValue);
+
+ String newOptionNewValue = "Some New Value";
+ changeNewOption(newOptionNewValue);
+
+ openProgram();
+
+ optionsDialog = invokeAnalysisDialog();
+
+ // check options displayed in the dialog
+ assertOnlyNewOptionsInUi();
+ assertOptionValue(NEW_OPTION_NAME, newOptionNewValue);
+ assertOptionValue(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE);
+ assertOldValueRemoved();
+ }
+
+ @Test
+ public void testOptionReplacing_DefaultNewValue_NonDefaultOldValue_DifferentValueTypes()
+ throws Exception {
+
+ //
+ // The old option's non-default value should be applied to the new option. The new option
+ // has a different object type than the old option.
+ //
+
+ installAnalyzer(ConvertValueTypeTestAnalyzerStub.class);
+
+ // install old options; the default value will not be used in the new option
+ installOldOptions(OLD_OPTION_DEFAULT_VALUE);
+ String newValue = "255,175,175"; // pink
+ changeOldOption(newValue);
+
+ openProgram();
+
+ optionsDialog = invokeAnalysisDialog();
+
+ // check options displayed in the dialog
+ assertOnlyNewOptionsInUi();
+ assertOptionValue(NEW_OPTION_NAME, toColor(newValue));
+ assertOptionValue(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE);
+ }
+
+//==================================================================================================
+// Private Methods
+//==================================================================================================
+
+ private void openProgram() {
+ ProgramManager pm = tool.getService(ProgramManager.class);
+ pm.openProgram(program.getDomainFile());
+ }
+
+ private void installOldOptions(Object value) {
+ Options programAnalysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ Options options = programAnalysisOptions.getOptions(AbstractTestAnalyzerStub.NAME);
+ AbstractOptions abstractOptions = (AbstractOptions) getInstanceField("options", options);
+
+ // this call creates an 'unregistered option'
+ String fullOptionName =
+ "Analyzers." + AbstractTestAnalyzerStub.NAME + '.' + OLD_OPTION_NAME;
+ Option option = abstractOptions.getOption(fullOptionName, OptionType.getOptionType(value),
+ OLD_OPTION_DEFAULT_VALUE);
+
+ // during testing 'value' may or may not match the default value
+ tx(program, () -> {
+ option.setCurrentValue(value);
+ setInstanceField("isRegistered", option, false);
+ });
+ }
+
+ private void installAnalyzer(Class extends Analyzer> analyzer) {
+
+ @SuppressWarnings("unchecked")
+ List> extensions =
+ (List>) getInstanceField("extensionPoints", ClassSearcher.class);
+
+ // remove any traces of previous test runs
+ extensions.removeIf(c -> c.getSimpleName().contains("TestAnalyzerStub"));
+
+ extensions.add(analyzer);
+ }
+
+ private AnalysisOptionsDialog invokeAnalysisDialog() {
+
+ CodeBrowserPlugin cbp = env.getPlugin(CodeBrowserPlugin.class);
+ CodeViewerProvider provider = cbp.getProvider();
+ DockingActionIf action = getAction(tool, "Auto Analyze");
+ ActionContext context = runSwing(() -> provider.getActionContext(null));
+ performAction(action, context, false);
+
+ // TODO temp debug to catch issue seen when running parallel tests
+ try {
+ return waitForDialogComponent(AnalysisOptionsDialog.class);
+ }
+ catch (Throwable t) {
+
+ printOpenWindows();
+
+ failWithException("Unable to find analysis dialog", t);
+ return null; // can't get here
+ }
+ }
+
+ private void changeNewOption(String newValue) {
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ Options analyzerOptions = options.getOptions(AbstractTestAnalyzerStub.NAME);
+
+ tx(program, () -> analyzerOptions.putObject(NEW_OPTION_NAME, newValue));
+ }
+
+ private void changeOldOption(String newValue) {
+ Options programAnalysisOptions = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ Options options = programAnalysisOptions.getOptions(AbstractTestAnalyzerStub.NAME);
+ AbstractOptions abstractOptions = (AbstractOptions) getInstanceField("options", options);
+
+ // this call creates an 'unregistered option'
+ String fullOptionName =
+ "Analyzers." + AbstractTestAnalyzerStub.NAME + '.' + OLD_OPTION_NAME;
+ Option option = abstractOptions.getOption(fullOptionName,
+ OptionType.getOptionType(OLD_OPTION_DEFAULT_VALUE), OLD_OPTION_DEFAULT_VALUE);
+
+ // during testing 'value' may or may not match the default value
+ tx(program, () -> {
+ option.setCurrentValue(newValue);
+ setInstanceField("isRegistered", option, false);
+ });
+ }
+
+ private void assertOldValueRemoved() {
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ Options analyzerOptions = options.getOptions(AbstractTestAnalyzerStub.NAME);
+ assertFalse("Old option not removed", analyzerOptions.contains(OLD_OPTION_NAME));
+ }
+
+ private void assertOnlyNewOptionsInUi() {
+
+ // click our analyzer in the list of options
+ selectAnalyzer(AbstractTestAnalyzerStub.NAME);
+
+ // get the panel of options
+ JPanel panel =
+ (JPanel) findComponentByName(optionsDialog, AnalysisPanel.ANALYZER_OPTIONS_PANEL_NAME);
+
+ // get the option labels
+ List uiOptionNames = new ArrayList<>();
+ Component[] components = panel.getComponents();
+ for (Component component : components) {
+
+ DefaultOptionComponent doc = (DefaultOptionComponent) component;
+ String text = doc.getLabelText();
+ uiOptionNames.add(text);
+ }
+
+ // check against our new options
+ assertEquals(2, uiOptionNames.size());
+ assertTrue(uiOptionNames.contains(UNCHANGING_OPTION_NAME));
+ assertTrue(uiOptionNames.contains(NEW_OPTION_NAME));
+ }
+
+ private void assertOptionValue(String optionName, Object defaultValue) {
+ Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
+ Options analyzerOptions = options.getOptions(AbstractTestAnalyzerStub.NAME);
+ Object value = analyzerOptions.getObject(optionName, null);
+ assertEquals("Option value is not as expected for '" + optionName + "'", defaultValue,
+ value);
+ }
+
+ private static Color toColor(String rgbString) {
+ String[] parts = rgbString.split(",");
+ int r = Integer.parseInt(parts[0]);
+ int g = Integer.parseInt(parts[1]);
+ int b = Integer.parseInt(parts[2]);
+ return new Color(r, g, b);
+ }
+
+ private void selectAnalyzer(String name) {
+ GTable table = getAnalyzerTable();
+ int analyzerRow = getRowForAnalyzer(name, table.getModel());
+ runSwing(() -> table.selectRow(analyzerRow));
+ }
+
+ private GTable getAnalyzerTable() {
+ // The analysis dialog uses a table to display the enablement and name of each analyzer
+ AnalysisPanel panel = (AnalysisPanel) getInstanceField("panel", optionsDialog);
+ return (GTable) getInstanceField("table", panel);
+ }
+
+ private int getRowForAnalyzer(String name, TableModel model) {
+ int rowCount = model.getRowCount();
+ int row = 0;
+ for (row = 0; row < rowCount; row++) {
+ String rowName = (String) model.getValueAt(row, 1);
+ if (name.equals(rowName)) {
+ break;// found it
+ }
+ }
+
+ if (row == rowCount) {
+ Assert.fail("Couldn't find analyzer named " + name);
+ }
+ int analyzerRow = row;
+ return analyzerRow;
+ }
+
+//==================================================================================================
+// Inner Classes
+//==================================================================================================
+
+ public static abstract class AbstractTestAnalyzerStub implements Analyzer {
+
+ protected AnalysisOptionsUpdater updater = new AnalysisOptionsUpdater();
+
+ private static final String NAME = "Test Analyzer";
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public AnalyzerType getAnalysisType() {
+ return AnalyzerType.FUNCTION_ANALYZER;
+ }
+
+ @Override
+ public boolean getDefaultEnablement(Program p) {
+ return true;
+ }
+
+ @Override
+ public boolean supportsOneTimeAnalysis() {
+ return false;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Test analyzer...";
+ }
+
+ @Override
+ public AnalysisPriority getPriority() {
+ return AnalysisPriority.FUNCTION_ANALYSIS;
+ }
+
+ @Override
+ public boolean canAnalyze(Program p) {
+ return true;
+ }
+
+ @Override
+ public boolean added(Program p, AddressSetView set, TaskMonitor monitor,
+ MessageLog log) throws CancelledException {
+ return false;
+ }
+
+ @Override
+ public boolean removed(Program p, AddressSetView set, TaskMonitor monitor,
+ MessageLog log) throws CancelledException {
+ return false;
+ }
+
+ @Override
+ public void registerOptions(Options options, Program p) {
+
+ options.registerOption(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE, null,
+ "Unchanging option description");
+
+ // replaces "Old Name"
+ options.registerOption(NEW_OPTION_NAME, NEW_OPTION_DEFAULT_VALUE, null,
+ "New option description");
+ }
+
+ @Override
+ public AnalysisOptionsUpdater getOptionsUpdater() {
+ return updater;
+ }
+
+ @Override
+ public void optionsChanged(Options options, Program p) {
+ // stub
+ }
+
+ @Override
+ public void analysisEnded(Program p) {
+ // stub
+ }
+
+ @Override
+ public boolean isPrototype() {
+ return false;
+ }
+ }
+
+ public static class NotReplacingTestAnalyzerStub extends AbstractTestAnalyzerStub {
+
+ public NotReplacingTestAnalyzerStub() {
+ super();
+
+ updater.registerReplacement(NEW_OPTION_NAME, OLD_OPTION_NAME, oldValue -> {
+ throw new AssertException(
+ "Replace function unexpectedly called for new/old options: '" +
+ NEW_OPTION_NAME + "' / '" + OLD_OPTION_NAME + "'");
+ });
+ }
+ }
+
+ public static class UseOldValueTestAnalyzerStub extends AbstractTestAnalyzerStub {
+
+ public UseOldValueTestAnalyzerStub() {
+ super();
+
+ updater.registerReplacement(NEW_OPTION_NAME, OLD_OPTION_NAME, oldValue -> {
+ return oldValue;
+ });
+ }
+ }
+
+ public static class ConvertValueTypeTestAnalyzerStub extends AbstractTestAnalyzerStub {
+
+ public ConvertValueTypeTestAnalyzerStub() {
+ super();
+
+ updater.registerReplacement(NEW_OPTION_NAME, OLD_OPTION_NAME, oldValue -> {
+ // Assumption: 'oldValue' is an RGB string; the new value expects a Color
+ return toColor((String) oldValue);
+ });
+ }
+
+ @Override
+ public void registerOptions(Options options, Program p) {
+
+ options.registerOption(UNCHANGING_OPTION_NAME, UNCHANGING_OPTION_DEFAULT_VALUE, null,
+ "Unchanging option description");
+
+ // replaces "Old Name"
+ options.registerOption(NEW_OPTION_NAME, NEW_OPTION_DEFAULT_VALUE_AS_COLOR, null,
+ "New option description");
+
+ }
+ }
+}
diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptionsTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptionsTest.java
index 40e8eaff8b..80574ad150 100644
--- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptionsTest.java
+++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/analysis/AnalysisOptionsTest.java
@@ -72,7 +72,6 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
@After
public void tearDown() throws Exception {
- env.release(program);
env.dispose();
cleanUpStoredPreferences();
}
@@ -258,6 +257,7 @@ public class AnalysisOptionsTest extends AbstractGhidraHeadedIntegrationTest {
//==================================================================================================
// Private Methods
//==================================================================================================
+
private void createConfig(String name, boolean stackOn, boolean refOn, boolean stringOn) {
setAnalyzerEnabled("Stack", stackOn);
setAnalyzerEnabled("Reference", refOn);
diff --git a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java
index aeabd0b6ea..79ec820a03 100644
--- a/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java
+++ b/Ghidra/Features/FunctionID/src/main/java/ghidra/feature/fid/analyzer/FidAnalyzer.java
@@ -15,6 +15,7 @@
*/
package ghidra.feature.fid.analyzer;
+import ghidra.app.plugin.core.analysis.AnalysisOptionsUpdater;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
@@ -42,7 +43,8 @@ public class FidAnalyzer extends AbstractAnalyzer {
private static final String OPTION_DESCRIPTION_CREATE_BOOKMARKS =
"If checked, an analysis bookmark will be created for each function which was matched " +
"against one or more known library functions.";
- public static final String APPLY_ALL_FID_LABELS_OPTION_NAME = "Always apply FID labels";
+
+ public static final String APPLY_ALL_FID_LABELS_OPTION_NAME = "Always Apply FID Labels";
private static final String APPLY_ALL_FID_LABELS_OPTION_DESCRIPTION = "Enable this option to " +
"always apply FID labels at functions regardless of existing labels at that function." +
" When enabled, FID labels will always be added." +
@@ -57,18 +59,33 @@ public class FidAnalyzer extends AbstractAnalyzer {
private boolean alwaysApplyFidLabels = APPLY_ALL_FID_LABELS_DEFAULT;
private boolean createBookmarksEnabled = OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED;
- public static final String SCORE_THRESHOLD_OPTION_NAME = "Instruction count threshold";
+ private static final String SCORE_THRESHOLD_OPTION_NAME = "Instruction Count Threshold";
+
private static final String SCORE_THRESHOLD_OPTION_DESCRIPTION =
"The minimum score that a potential match must meet to be labeled by the analyzer. " +
"Score corresponds roughly to the number of instructions in the function.";
private float scoreThreshold;
- public static final String MULTIMATCH_THRESHOLD_OPTION_NAME = "Multiple match threshold";
+ private static final String MULTIMATCH_THRESHOLD_OPTION_NAME = "Multiple Match Threshold";
private static final String MULTIMATCH_THRESHOLD_OPTION_DESCRIPTION =
"If there are multiple conflicting matches for a function, its score must exceed " +
"this secondary threshold in order to be labeled by the analyzer";
private float multiScoreThreshold;
+//==================================================================================================
+// Old Option Names - Should stick around for multiple major versions after 10.2
+//==================================================================================================
+
+ private static final String SCORE_THRESHOLD_OPTION_NAME_OLD = "Instruction count threshold";
+ private static final String MULTIMATCH_THRESHOLD_OPTION_NAME_OLD = "Multiple match threshold";
+ private static final String APPLY_ALL_FID_LABELS_OPTION_NAME_OLD = "Always apply FID labels";
+
+ private AnalysisOptionsUpdater optionsUpdater = new AnalysisOptionsUpdater();
+
+//==================================================================================================
+// End Old Option Names
+//==================================================================================================
+
public FidAnalyzer() {
/*
* FID is listed as a byte analyzer because we don't want to run it all the time. It
@@ -86,6 +103,13 @@ public class FidAnalyzer extends AbstractAnalyzer {
setPriority(AnalysisPriority.FUNCTION_ID_ANALYSIS.before());
scoreThreshold = service.getDefaultScoreThreshold();
multiScoreThreshold = service.getDefaultMultiNameThreshold();
+
+ optionsUpdater.registerReplacement(SCORE_THRESHOLD_OPTION_NAME,
+ SCORE_THRESHOLD_OPTION_NAME_OLD);
+ optionsUpdater.registerReplacement(MULTIMATCH_THRESHOLD_OPTION_NAME,
+ MULTIMATCH_THRESHOLD_OPTION_NAME_OLD);
+ optionsUpdater.registerReplacement(APPLY_ALL_FID_LABELS_OPTION_NAME,
+ APPLY_ALL_FID_LABELS_OPTION_NAME_OLD);
}
@Override
@@ -112,8 +136,9 @@ public class FidAnalyzer extends AbstractAnalyzer {
// Name Change can change the nature of a function from a system
// library. Probably a better way to do this.
- AutoAnalysisManager.getAnalysisManager(program).functionModifierChanged(
- cmd.getFIDLocations());
+ AutoAnalysisManager.getAnalysisManager(program)
+ .functionModifierChanged(
+ cmd.getFIDLocations());
return true;
}
@@ -129,6 +154,11 @@ public class FidAnalyzer extends AbstractAnalyzer {
OPTION_DESCRIPTION_CREATE_BOOKMARKS);
}
+ @Override
+ public AnalysisOptionsUpdater getOptionsUpdater() {
+ return optionsUpdater;
+ }
+
@Override
public void optionsChanged(Options options, Program program) {
scoreThreshold =
diff --git a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/DefaultOptionComponent.java b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/DefaultOptionComponent.java
index 9e821c42ba..d6bdcfed9d 100644
--- a/Ghidra/Framework/Docking/src/main/java/docking/options/editor/DefaultOptionComponent.java
+++ b/Ghidra/Framework/Docking/src/main/java/docking/options/editor/DefaultOptionComponent.java
@@ -81,4 +81,8 @@ public class DefaultOptionComponent extends GenericOptionsComponent {
int maxHeight = Math.max(dimension.height, component.getPreferredSize().height);
return new Dimension(dimension.width, maxHeight);
}
+
+ public String getLabelText() {
+ return label.getText();
+ }
}
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java
index 9d10a471aa..256d38df63 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/framework/options/Options.java
@@ -35,7 +35,7 @@ public interface Options {
*
* @return String
*/
- public abstract String getName();
+ public String getName();
/**
* Returns a unique id for option in this options with the given name. This will be the full
@@ -89,13 +89,13 @@ public interface Options {
* Set the location for where help can be found for this entire options object.
* @param helpLocation location for help on the option
*/
- public abstract void setOptionsHelpLocation(HelpLocation helpLocation);
+ public void setOptionsHelpLocation(HelpLocation helpLocation);
/**
* Returns the HelpLocation for this entire Options object.
* @return the HelpLocation for this entire Options object.
*/
- public abstract HelpLocation getOptionsHelpLocation();
+ public HelpLocation getOptionsHelpLocation();
/**
* Get the location for where help can be found for the option with
@@ -103,7 +103,7 @@ public interface Options {
* @param optionName name of the option
* @return null if the help location was not set on the option
*/
- public abstract HelpLocation getHelpLocation(String optionName);
+ public HelpLocation getHelpLocation(String optionName);
/**
* Registers an option with a description, help location, and a default value without specifying
@@ -117,7 +117,7 @@ public interface Options {
* @param description a description of the option.
* @throws IllegalArgumentException if the defaultValue is null
*/
- public abstract void registerOption(String optionName, Object defaultValue, HelpLocation help,
+ public void registerOption(String optionName, Object defaultValue, HelpLocation help,
String description);
/**
@@ -130,7 +130,7 @@ public interface Options {
* @param help the HelpLocation for this option.
* @param description a description of the option.
*/
- public abstract void registerOption(String optionName, OptionType type, Object defaultValue,
+ public void registerOption(String optionName, OptionType type, Object defaultValue,
HelpLocation help, String description);
/**
@@ -146,20 +146,20 @@ public interface Options {
* then the property editor can't be null;
* @throws IllegalStateException if the options is a custom option and the editor is null.
*/
- public abstract void registerOption(String optionName, OptionType type, Object defaultValue,
+ public void registerOption(String optionName, OptionType type, Object defaultValue,
HelpLocation help, String description, PropertyEditor editor);
/**
* Register the options editor that will handle the editing for all the options or a sub group of options.
* @param editor the custom editor panel to be used to edit the options or sub group of options.
*/
- public abstract void registerOptionsEditor(OptionsEditor editor);
+ public void registerOptionsEditor(OptionsEditor editor);
/**
* Get the editor that will handle editing all the values in this options or sub group of options.
* @return null if no options editor was registered
*/
- public abstract OptionsEditor getOptionsEditor();
+ public OptionsEditor getOptionsEditor();
/**
* Put the object value. If the option exists, the type must match the type of the existing
@@ -169,7 +169,7 @@ public interface Options {
* @throws IllegalStateException if the object does not match the existing type of the option.
* @throws IllegalArgumentException if the object is null or not a supported type.
*/
- public abstract void putObject(String optionName, Object obj);
+ public void putObject(String optionName, Object obj);
/**
* Get the object value; called when the options dialog is being
@@ -179,7 +179,7 @@ public interface Options {
* @return object with the given option name; if no option was found,
* return default value (this value is not stored in the option maps)
*/
- public abstract Object getObject(String optionName, Object defaultValue);
+ public Object getObject(String optionName, Object defaultValue);
/**
* Get the boolean value for the given option name.
@@ -188,7 +188,7 @@ public interface Options {
* is no option with the given name.
* @return boolean option value
*/
- public abstract boolean getBoolean(String optionName, boolean defaultValue);
+ public boolean getBoolean(String optionName, boolean defaultValue);
/**
* Get the byte array for the given option name.
@@ -197,7 +197,7 @@ public interface Options {
* is no option with the given name
* @return byte[] byte array value
*/
- public abstract byte[] getByteArray(String optionName, byte[] defaultValue);
+ public byte[] getByteArray(String optionName, byte[] defaultValue);
/**
* Get the int value for the given option name.
@@ -206,7 +206,7 @@ public interface Options {
* is no option with the given name
* @return int option value
*/
- public abstract int getInt(String optionName, int defaultValue);
+ public int getInt(String optionName, int defaultValue);
/**
* Get the double value for the given option name.
@@ -215,7 +215,7 @@ public interface Options {
* is no option with the given name
* @return double value for the option
*/
- public abstract double getDouble(String optionName, double defaultValue);
+ public double getDouble(String optionName, double defaultValue);
/**
* Get the float value for the given option name.
@@ -224,7 +224,7 @@ public interface Options {
* is no option with the given name
* @return float value for the option
*/
- public abstract float getFloat(String optionName, float defaultValue);
+ public float getFloat(String optionName, float defaultValue);
/**
* Get the long value for the given option name.
@@ -233,7 +233,7 @@ public interface Options {
* is no option with the given name
* @return long value for the option
*/
- public abstract long getLong(String optionName, long defaultValue);
+ public long getLong(String optionName, long defaultValue);
/**
* Get the custom option value for the given option name.
@@ -242,7 +242,7 @@ public interface Options {
* is no option with the given name
* @return WrappedOption value for the option
*/
- public abstract CustomOption getCustomOption(String optionName, CustomOption defaultValue);
+ public CustomOption getCustomOption(String optionName, CustomOption defaultValue);
/**
* Get the Color for the given option name.
@@ -253,7 +253,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Color
*/
- public abstract Color getColor(String optionName, Color defaultValue);
+ public Color getColor(String optionName, Color defaultValue);
/**
* Get the File for the given option name.
@@ -264,7 +264,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a File options
*/
- public abstract File getFile(String optionName, File defaultValue);
+ public File getFile(String optionName, File defaultValue);
/**
* Get the Date for the given option name.
@@ -275,7 +275,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Date options
*/
- public abstract Date getDate(String pName, Date date);
+ public Date getDate(String pName, Date date);
/**
* Get the Font for the given option name.
@@ -286,7 +286,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a Font
*/
- public abstract Font getFont(String optionName, Font defaultValue);
+ public Font getFont(String optionName, Font defaultValue);
/**
* Get the KeyStrokg for the given action name.
@@ -297,7 +297,7 @@ public interface Options {
* @throws IllegalArgumentException is a option exists with the given
* name but it is not a KeyStroke
*/
- public abstract KeyStroke getKeyStroke(String optionName, KeyStroke defaultValue);
+ public KeyStroke getKeyStroke(String optionName, KeyStroke defaultValue);
/**
* Get the string value for the given option name.
@@ -306,7 +306,7 @@ public interface Options {
* option with the given name
* @return String value for the option
*/
- public abstract String getString(String optionName, String defaultValue);
+ public String getString(String optionName, String defaultValue);
/**
* Get the Enum value for the given option name.
@@ -315,70 +315,70 @@ public interface Options {
* no option with the given name
* @return Enum value for the option
*/
- public abstract > T getEnum(String optionName, T defaultValue);
+ public > T getEnum(String optionName, T defaultValue);
/**
* Sets the long value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setLong(String optionName, long value);
+ public void setLong(String optionName, long value);
/**
* Sets the boolean value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setBoolean(String optionName, boolean value);
+ public void setBoolean(String optionName, boolean value);
/**
* Sets the int value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setInt(String optionName, int value);
+ public void setInt(String optionName, int value);
/**
* Sets the double value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setDouble(String optionName, double value);
+ public void setDouble(String optionName, double value);
/**
* Sets the float value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setFloat(String optionName, float value);
+ public void setFloat(String optionName, float value);
/**
* Sets the Custom option value for the option.
* @param optionName name of the option
* @param value the value
*/
- public abstract void setCustomOption(String optionName, CustomOption value);
+ public void setCustomOption(String optionName, CustomOption value);
/**
* Sets the byte[] value for the given option name.
* @param optionName the name of the option on which to save bytes.
* @param value the value
*/
- public abstract void setByteArray(String optionName, byte[] value);
+ public void setByteArray(String optionName, byte[] value);
/**
* Sets the File value for the option.
* @param optionName name of the option
* @param value the value
*/
- public abstract void setFile(String optionName, File value);
+ public void setFile(String optionName, File value);
/**
* Sets the Date value for the option.
* @param optionName name of the option
* @param newSetting the Date to set
*/
- public abstract void setDate(String optionName, Date newSetting);
+ public void setDate(String optionName, Date newSetting);
/**
* Sets the Color value for the option
@@ -387,7 +387,7 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a Color
*/
- public abstract void setColor(String optionName, Color value);
+ public void setColor(String optionName, Color value);
/**
* Sets the Font value for the option
@@ -396,7 +396,7 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a Font
*/
- public abstract void setFont(String optionName, Font value);
+ public void setFont(String optionName, Font value);
/**
* Sets the KeyStroke value for the option
@@ -405,27 +405,27 @@ public interface Options {
* @throws IllegalArgumentException if a option with the given
* name already exists, but it is not a KeyStroke
*/
- public abstract void setKeyStroke(String optionName, KeyStroke value);
+ public void setKeyStroke(String optionName, KeyStroke value);
/**
* Set the String value for the option.
* @param optionName name of the option
* @param value value of the option
*/
- public abstract void setString(String optionName, String value);
+ public void setString(String optionName, String value);
/**
* Set the Enum value for the option.
* @param optionName name of the option
* @param value Enum value of the option
*/
- public abstract > void setEnum(String optionName, T value);
+ public > void setEnum(String optionName, T value);
/**
* Remove the option name.
* @param optionName name of option to remove
*/
- public abstract void removeOption(String optionName);
+ public void removeOption(String optionName);
/**
* Get the list of option names. This method will return the names (paths) of all options contained
@@ -434,21 +434,21 @@ public interface Options {
* the "aaa" and "bbb" names.
* @return the list of all option names(paths) under this options.
*/
- public abstract List getOptionNames();
+ public List getOptionNames();
/**
* Return true if a option exists with the given name.
* @param optionName option name
* @return true if there exists an option with the given name
*/
- public abstract boolean contains(String optionName);
+ public boolean contains(String optionName);
/**
* Get the description for the given option name.
* @param optionName name of the option
* @return null if the description or option name does not exist
*/
- public abstract String getDescription(String optionName);
+ public String getDescription(String optionName);
/**
* Returns true if the specified option has been registered. Only registered names
@@ -456,21 +456,21 @@ public interface Options {
* @param optionName the option name
* @return true if registered
*/
- public abstract boolean isRegistered(String optionName);
+ public boolean isRegistered(String optionName);
/**
* Returns true if the option with the given name's current value is the default value.
* @param optionName the name of the option.
* @return true if the options has its current value equal to its default value.
*/
- public abstract boolean isDefaultValue(String optionName);
+ public boolean isDefaultValue(String optionName);
/**
* Restores all options contained herein to their default values.
*
* @see #restoreDefaultValue(String)
*/
- public abstract void restoreDefaultValues();
+ public void restoreDefaultValues();
/**
* Restores the option denoted by the given name to its default value.
@@ -478,7 +478,7 @@ public interface Options {
* @param optionName The name of the option to restore
* @see #restoreDefaultValues()
*/
- public abstract void restoreDefaultValue(String optionName);
+ public void restoreDefaultValue(String optionName);
/**
* Returns a Options object that is a sub-options of this options.
@@ -520,14 +520,14 @@ public interface Options {
* @param name the name of the option for which to retrieve the value as a string
* @return the value as a string for the given option.
*/
- public abstract String getValueAsString(String name);
+ public String getValueAsString(String name);
/**
* Returns the default value as a string for the given option.
* @param optionName the name of the option for which to retrieve the default value as a string
* @return the default value as a string for the given option.
*/
- public abstract String getDefaultValueAsString(String optionName);
+ public String getDefaultValueAsString(String optionName);
/**
* Returns true if the two options objects have the same set of options and values
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/constraint/PropertyConstraint.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/constraint/PropertyConstraint.java
index a1647431fb..6cbf28229c 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/constraint/PropertyConstraint.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/util/constraint/PropertyConstraint.java
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
- * REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +15,8 @@
*/
package ghidra.util.constraint;
+import java.util.Objects;
+
import generic.constraint.ConstraintData;
import ghidra.program.model.listing.Program;
import ghidra.util.SystemUtilities;
@@ -26,8 +27,8 @@ public class PropertyConstraint extends ProgramConstraint {
super("property");
}
- private String name; // name of the program property to constrain
- private String value; // value the property should take
+ private String name; // name of the program property to constrain
+ private String value; // value the property should take
@Override
public boolean isSatisfied(Program program) {
@@ -41,6 +42,11 @@ public class PropertyConstraint extends ProgramConstraint {
value = data.getString("value");
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, value);
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof PropertyConstraint)) {
@@ -54,5 +60,4 @@ public class PropertyConstraint extends ProgramConstraint {
public String getDescription() {
return "property " + name + " = " + value;
}
-
}